Spaces:
Build error
Build error
Upload 5 files
Browse files- README.md +11 -8
- app.py +564 -0
- assets/.gitkeep +1 -0
- requirements.txt +6 -0
- sample_data/training_catalog.json +6 -0
README.md
CHANGED
|
@@ -1,14 +1,17 @@
|
|
| 1 |
---
|
| 2 |
-
title: ASHU AI
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version:
|
| 8 |
-
python_version: '3.13'
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
| 11 |
-
|
| 12 |
---
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: ASHU AI Mentor Studio
|
| 3 |
+
emoji: 🎓
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: indigo
|
| 6 |
sdk: gradio
|
| 7 |
+
sdk_version: 4.44.1
|
|
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
+
license: mit
|
| 11 |
---
|
| 12 |
|
| 13 |
+
# ASHU AI Mentor Studio
|
| 14 |
+
|
| 15 |
+
A Hugging Face native Gradio app for resume/JD analysis, ATS scoring, interview preparation, training plans, document summarization, quiz generation, presenter scripts, scorecards, and architecture diagrams.
|
| 16 |
+
|
| 17 |
+
This version uses no Docker and no GPU-heavy runtime so it is safer for CPU Basic Spaces.
|
app.py
ADDED
|
@@ -0,0 +1,564 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import csv
|
| 4 |
+
import datetime as dt
|
| 5 |
+
import io
|
| 6 |
+
import json
|
| 7 |
+
import math
|
| 8 |
+
import os
|
| 9 |
+
import re
|
| 10 |
+
import tempfile
|
| 11 |
+
import textwrap
|
| 12 |
+
import zipfile
|
| 13 |
+
from collections import Counter
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
from typing import Dict, List, Tuple
|
| 16 |
+
|
| 17 |
+
import gradio as gr
|
| 18 |
+
import numpy as np
|
| 19 |
+
import pandas as pd
|
| 20 |
+
|
| 21 |
+
try:
|
| 22 |
+
import PyPDF2
|
| 23 |
+
except Exception:
|
| 24 |
+
PyPDF2 = None
|
| 25 |
+
|
| 26 |
+
try:
|
| 27 |
+
import docx
|
| 28 |
+
except Exception:
|
| 29 |
+
docx = None
|
| 30 |
+
|
| 31 |
+
try:
|
| 32 |
+
from reportlab.lib import colors
|
| 33 |
+
from reportlab.lib.pagesizes import A4
|
| 34 |
+
from reportlab.lib.styles import getSampleStyleSheet
|
| 35 |
+
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
|
| 36 |
+
except Exception:
|
| 37 |
+
SimpleDocTemplate = None
|
| 38 |
+
|
| 39 |
+
APP_TITLE = "ASHU AI Mentor Studio"
|
| 40 |
+
BASE_DIR = Path(__file__).parent.resolve()
|
| 41 |
+
OUT_DIR = BASE_DIR / "outputs"
|
| 42 |
+
OUT_DIR.mkdir(exist_ok=True)
|
| 43 |
+
|
| 44 |
+
SKILLS = sorted(set('''
|
| 45 |
+
python java c++ javascript typescript sql mysql postgresql mongodb snowflake excel powerbi tableau pandas numpy scipy scikit-learn sklearn tensorflow pytorch keras opencv yolo ultralytics nlp computer vision machine learning deep learning llm rag langchain llamaindex openai huggingface transformers gradio streamlit flask fastapi docker kubernetes git github linux bash aws azure gcp airflow mlflow dvc ci/cd rest api microservices prompt engineering data analysis data science statistics regression classification clustering xgboost lightgbm random forest svm cnn rnn lstm transformer bert gpt vector database faiss chromadb pinecone elasticsearch selenium html css react nodejs django pytest monitoring grafana prometheus agile scrum communication leadership documentation research writing latex
|
| 46 |
+
'''.split()))
|
| 47 |
+
|
| 48 |
+
STOPWORDS = set('''
|
| 49 |
+
a an the and or but if then else with without for from into onto in on at by to of is are was were be been being this that these those as it its your you we our they them their candidate role job work experience skills using use used will can should could would may might about across within strong good excellent ability knowledge hands-on years year team teams project projects responsibilities requirements preferred required
|
| 50 |
+
'''.split())
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def clean_text(text: str) -> str:
|
| 54 |
+
text = text or ""
|
| 55 |
+
text = re.sub(r"\s+", " ", text)
|
| 56 |
+
return text.strip()
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def tokens(text: str) -> List[str]:
|
| 60 |
+
return [t.lower() for t in re.findall(r"[A-Za-z][A-Za-z0-9+.#/-]{1,}", text or "")]
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def keywords(text: str, n: int = 30) -> List[str]:
|
| 64 |
+
toks = [t for t in tokens(text) if t not in STOPWORDS and len(t) > 2]
|
| 65 |
+
counts = Counter(toks)
|
| 66 |
+
return [w for w, _ in counts.most_common(n)]
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def detect_skills(text: str) -> List[str]:
|
| 70 |
+
hay = " " + re.sub(r"[^a-z0-9+#./-]+", " ", (text or "").lower()) + " "
|
| 71 |
+
found = []
|
| 72 |
+
for s in SKILLS:
|
| 73 |
+
pattern = r"(?<![a-z0-9+#./-])" + re.escape(s.lower()) + r"(?![a-z0-9+#./-])"
|
| 74 |
+
if re.search(pattern, hay):
|
| 75 |
+
found.append(s)
|
| 76 |
+
return sorted(found)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def read_file(file_obj) -> str:
|
| 80 |
+
if file_obj is None:
|
| 81 |
+
return ""
|
| 82 |
+
path = Path(file_obj.name if hasattr(file_obj, "name") else str(file_obj))
|
| 83 |
+
suffix = path.suffix.lower()
|
| 84 |
+
try:
|
| 85 |
+
if suffix == ".pdf" and PyPDF2 is not None:
|
| 86 |
+
text_parts = []
|
| 87 |
+
with open(path, "rb") as f:
|
| 88 |
+
reader = PyPDF2.PdfReader(f)
|
| 89 |
+
for page in reader.pages:
|
| 90 |
+
text_parts.append(page.extract_text() or "")
|
| 91 |
+
return clean_text("\n".join(text_parts))
|
| 92 |
+
if suffix in [".docx", ".doc"] and docx is not None:
|
| 93 |
+
d = docx.Document(str(path))
|
| 94 |
+
return clean_text("\n".join(p.text for p in d.paragraphs))
|
| 95 |
+
if suffix in [".txt", ".md", ".csv"]:
|
| 96 |
+
return clean_text(path.read_text(encoding="utf-8", errors="ignore"))
|
| 97 |
+
return clean_text(path.read_text(encoding="utf-8", errors="ignore"))
|
| 98 |
+
except Exception as e:
|
| 99 |
+
return f"[Could not read file: {e}]"
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def safe_filename(prefix: str, ext: str) -> str:
|
| 103 |
+
ts = dt.datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 104 |
+
return str(OUT_DIR / f"{prefix}_{ts}.{ext}")
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def write_text_file(prefix: str, content: str, ext: str = "txt") -> str:
|
| 108 |
+
path = safe_filename(prefix, ext)
|
| 109 |
+
Path(path).write_text(content or "", encoding="utf-8")
|
| 110 |
+
return path
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def write_csv(prefix: str, rows: List[Dict[str, str]]) -> str:
|
| 114 |
+
path = safe_filename(prefix, "csv")
|
| 115 |
+
if not rows:
|
| 116 |
+
rows = [{"message": "No rows"}]
|
| 117 |
+
with open(path, "w", newline="", encoding="utf-8") as f:
|
| 118 |
+
writer = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
|
| 119 |
+
writer.writeheader()
|
| 120 |
+
writer.writerows(rows)
|
| 121 |
+
return path
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def write_pdf(prefix: str, title: str, sections: List[Tuple[str, str]]) -> str:
|
| 125 |
+
path = safe_filename(prefix, "pdf")
|
| 126 |
+
if SimpleDocTemplate is None:
|
| 127 |
+
return write_text_file(prefix, "\n\n".join([title] + [f"{h}\n{b}" for h, b in sections]), "txt")
|
| 128 |
+
styles = getSampleStyleSheet()
|
| 129 |
+
doc = SimpleDocTemplate(path, pagesize=A4, rightMargin=36, leftMargin=36, topMargin=36, bottomMargin=36)
|
| 130 |
+
story = [Paragraph(title, styles["Title"]), Spacer(1, 12)]
|
| 131 |
+
for heading, body in sections:
|
| 132 |
+
story.append(Paragraph(heading, styles["Heading2"]))
|
| 133 |
+
for para in textwrap.wrap(clean_text(body), width=95) or [""]:
|
| 134 |
+
story.append(Paragraph(para, styles["BodyText"]))
|
| 135 |
+
story.append(Spacer(1, 6))
|
| 136 |
+
story.append(Spacer(1, 8))
|
| 137 |
+
doc.build(story)
|
| 138 |
+
return path
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def zip_files(prefix: str, files: List[str]) -> str:
|
| 142 |
+
path = safe_filename(prefix, "zip")
|
| 143 |
+
with zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED) as z:
|
| 144 |
+
for f in files:
|
| 145 |
+
if f and Path(f).exists():
|
| 146 |
+
z.write(f, arcname=Path(f).name)
|
| 147 |
+
return path
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def similarity_score(a: str, b: str) -> float:
|
| 151 |
+
ka, kb = set(keywords(a, 100)), set(keywords(b, 100))
|
| 152 |
+
if not ka or not kb:
|
| 153 |
+
return 0.0
|
| 154 |
+
return len(ka & kb) / max(1, len(kb))
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def ats_analyze(resume_file, resume_text: str, jd_text: str, target_role: str):
|
| 158 |
+
resume = clean_text((resume_text or "") + "\n" + read_file(resume_file))
|
| 159 |
+
jd = clean_text(jd_text or "")
|
| 160 |
+
if not resume:
|
| 161 |
+
return "Upload/paste a resume first.", None
|
| 162 |
+
if not jd:
|
| 163 |
+
return "Paste the job description first.", None
|
| 164 |
+
|
| 165 |
+
r_skills = detect_skills(resume)
|
| 166 |
+
j_skills = detect_skills(jd)
|
| 167 |
+
matched = sorted(set(r_skills) & set(j_skills))
|
| 168 |
+
missing = sorted(set(j_skills) - set(r_skills))
|
| 169 |
+
extra = sorted(set(r_skills) - set(j_skills))[:25]
|
| 170 |
+
keyword_match = similarity_score(resume, jd)
|
| 171 |
+
skill_score = len(matched) / max(1, len(j_skills))
|
| 172 |
+
length_score = min(1.0, len(resume.split()) / 450)
|
| 173 |
+
role_bonus = 0.08 if target_role and target_role.lower() in resume.lower() else 0.0
|
| 174 |
+
score = round(100 * min(1.0, 0.55 * skill_score + 0.30 * keyword_match + 0.15 * length_score + role_bonus), 1)
|
| 175 |
+
verdict = "Strong match" if score >= 75 else "Moderate match" if score >= 55 else "Needs tailoring"
|
| 176 |
+
|
| 177 |
+
jd_k = keywords(jd, 20)
|
| 178 |
+
resume_k = keywords(resume, 20)
|
| 179 |
+
missing_kw = [k for k in jd_k if k not in resume_k][:15]
|
| 180 |
+
|
| 181 |
+
md = f"""
|
| 182 |
+
# ATS Resume + JD Analysis
|
| 183 |
+
|
| 184 |
+
**Target role:** {target_role or 'Not specified'}
|
| 185 |
+
**ATS-style score:** **{score}/100**
|
| 186 |
+
**Verdict:** **{verdict}**
|
| 187 |
+
|
| 188 |
+
## Skill Coverage
|
| 189 |
+
- JD skills detected: {len(j_skills)}
|
| 190 |
+
- Resume skills detected: {len(r_skills)}
|
| 191 |
+
- Matched skills: {len(matched)}
|
| 192 |
+
- Missing JD skills: {len(missing)}
|
| 193 |
+
|
| 194 |
+
## Matched Skills
|
| 195 |
+
{', '.join(matched) if matched else 'No direct technical skill matches detected.'}
|
| 196 |
+
|
| 197 |
+
## Missing Skills to Add/Improve
|
| 198 |
+
{', '.join(missing) if missing else 'No major missing skills detected from the built-in skill library.'}
|
| 199 |
+
|
| 200 |
+
## Resume Skills Not Prominent in JD
|
| 201 |
+
{', '.join(extra) if extra else 'None detected.'}
|
| 202 |
+
|
| 203 |
+
## Missing JD Keywords
|
| 204 |
+
{', '.join(missing_kw) if missing_kw else 'No important missing JD keywords detected.'}
|
| 205 |
+
|
| 206 |
+
## Immediate Improvements
|
| 207 |
+
1. Add a short role-aligned summary using the exact job title.
|
| 208 |
+
2. Put matched technical skills in a dedicated skills section.
|
| 209 |
+
3. Add quantified project bullets: problem, model/tool, metric, business impact.
|
| 210 |
+
4. Add missing JD keywords only where truthful.
|
| 211 |
+
5. Keep resume simple: no tables, no image-only text, consistent section headings.
|
| 212 |
+
"""
|
| 213 |
+
rows = [
|
| 214 |
+
{"metric": "ATS score", "value": str(score)},
|
| 215 |
+
{"metric": "matched skills", "value": ", ".join(matched)},
|
| 216 |
+
{"metric": "missing skills", "value": ", ".join(missing)},
|
| 217 |
+
{"metric": "missing keywords", "value": ", ".join(missing_kw)},
|
| 218 |
+
]
|
| 219 |
+
csv_path = write_csv("ashu_ats_analysis", rows)
|
| 220 |
+
pdf_path = write_pdf("ashu_ats_analysis", "ASHU ATS Resume Analysis", [
|
| 221 |
+
("Summary", f"Target role: {target_role}. Score: {score}/100. Verdict: {verdict}."),
|
| 222 |
+
("Matched Skills", ", ".join(matched) or "None"),
|
| 223 |
+
("Missing Skills", ", ".join(missing) or "None"),
|
| 224 |
+
("Missing JD Keywords", ", ".join(missing_kw) or "None"),
|
| 225 |
+
("Recommendations", "Add role-aligned summary, quantified project bullets, missing truthful keywords, and clean ATS formatting."),
|
| 226 |
+
])
|
| 227 |
+
z = zip_files("ashu_ats_outputs", [csv_path, pdf_path])
|
| 228 |
+
return md, z
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
def generate_cover_letter(name: str, company: str, role: str, resume_file, resume_text: str, jd_text: str):
|
| 232 |
+
resume = clean_text((resume_text or "") + "\n" + read_file(resume_file))
|
| 233 |
+
jd = clean_text(jd_text or "")
|
| 234 |
+
r_skills = detect_skills(resume)
|
| 235 |
+
j_skills = detect_skills(jd)
|
| 236 |
+
matched = sorted(set(r_skills) & set(j_skills))[:10]
|
| 237 |
+
role = role or "the advertised role"
|
| 238 |
+
company = company or "your organization"
|
| 239 |
+
name = name or "Candidate"
|
| 240 |
+
body = f"""Dear Hiring Team,
|
| 241 |
+
|
| 242 |
+
I am writing to express my interest in the {role} position at {company}. My background aligns well with the role requirements, especially in {', '.join(matched[:6]) if matched else 'AI, analytics, software development, and problem solving'}.
|
| 243 |
+
|
| 244 |
+
Across my work, I have focused on building practical solutions that connect technical execution with measurable outcomes. I bring experience in understanding requirements, designing reliable workflows, implementing models or systems, evaluating results, and communicating insights clearly to stakeholders.
|
| 245 |
+
|
| 246 |
+
What attracts me to this opportunity is the chance to contribute to a team where technical depth, ownership, and impact matter. I would be excited to apply my experience to the problems your team is solving and to keep learning in a high-performance environment.
|
| 247 |
+
|
| 248 |
+
Thank you for considering my application. I would welcome the opportunity to discuss how my experience can contribute to {company}.
|
| 249 |
+
|
| 250 |
+
Sincerely,
|
| 251 |
+
{name}
|
| 252 |
+
"""
|
| 253 |
+
path = write_text_file("ashu_cover_letter", body, "txt")
|
| 254 |
+
return body, path
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
def interview_questions(role: str, skills: str, difficulty: str, count: int):
|
| 258 |
+
role = role or "AI/ML Engineer"
|
| 259 |
+
diff = difficulty or "Medium"
|
| 260 |
+
skill_list = [s.strip() for s in re.split(r"[,\n]", skills or "") if s.strip()]
|
| 261 |
+
if not skill_list:
|
| 262 |
+
skill_list = ["Python", "Machine Learning", "System Design", "Communication"]
|
| 263 |
+
qs = []
|
| 264 |
+
for i in range(1, int(count) + 1):
|
| 265 |
+
s = skill_list[(i - 1) % len(skill_list)]
|
| 266 |
+
if i % 4 == 1:
|
| 267 |
+
q = f"Explain a project where you used {s}. What was the problem, your approach, and the final impact?"
|
| 268 |
+
elif i % 4 == 2:
|
| 269 |
+
q = f"For a {role}, how would you design a reliable workflow using {s} in production?"
|
| 270 |
+
elif i % 4 == 3:
|
| 271 |
+
q = f"What can go wrong while using {s}, and how would you validate or debug it?"
|
| 272 |
+
else:
|
| 273 |
+
q = f"Give a {diff.lower()}-level scenario where {s} must be optimized for accuracy, latency, or cost."
|
| 274 |
+
qs.append({"No.": i, "Question": q, "Difficulty": diff, "Skill": s})
|
| 275 |
+
df = pd.DataFrame(qs)
|
| 276 |
+
csv_path = write_csv("ashu_interview_questions", qs)
|
| 277 |
+
md = "# Interview Questions\n\n" + "\n".join([f"{r['No.']}. **[{r['Skill']}]** {r['Question']}" for r in qs])
|
| 278 |
+
return md, csv_path
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
def score_answer(question: str, answer: str):
|
| 282 |
+
q = clean_text(question)
|
| 283 |
+
a = clean_text(answer)
|
| 284 |
+
if not q or not a:
|
| 285 |
+
return "Add both question and answer."
|
| 286 |
+
qk = set(keywords(q, 20))
|
| 287 |
+
ak = set(keywords(a, 50))
|
| 288 |
+
coverage = len(qk & ak) / max(1, len(qk))
|
| 289 |
+
structure = sum(1 for x in ["problem", "approach", "result", "impact", "metric", "learned"] if x in a.lower()) / 6
|
| 290 |
+
length = min(1.0, len(a.split()) / 120)
|
| 291 |
+
score = round(100 * (0.45 * coverage + 0.35 * structure + 0.20 * length), 1)
|
| 292 |
+
feedback = []
|
| 293 |
+
if structure < 0.5:
|
| 294 |
+
feedback.append("Use STAR format: Situation, Task, Action, Result.")
|
| 295 |
+
if coverage < 0.5:
|
| 296 |
+
feedback.append("Repeat important terms from the question and answer them directly.")
|
| 297 |
+
if len(a.split()) < 80:
|
| 298 |
+
feedback.append("Add more detail and one measurable result.")
|
| 299 |
+
if not feedback:
|
| 300 |
+
feedback.append("Good structure. Add one stronger metric if possible.")
|
| 301 |
+
return f"""# Answer Score: {score}/100
|
| 302 |
+
|
| 303 |
+
## Feedback
|
| 304 |
+
- """ + "\n- ".join(feedback) + "\n\n## Improved Answer Template\nI handled a situation where **[context]**. My task was **[responsibility]**. I used **[methods/tools]** to solve **[specific problem]**. The result was **[metric/business impact]**. From this, I learned **[lesson]**."
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
def training_plan(goal: str, level: str, weeks: int, hours_per_week: int):
|
| 308 |
+
goal = goal or "AI/ML and interview preparation"
|
| 309 |
+
level = level or "Beginner"
|
| 310 |
+
weeks = int(weeks)
|
| 311 |
+
hours_per_week = int(hours_per_week)
|
| 312 |
+
base_topics = keywords(goal, 10)
|
| 313 |
+
if not base_topics:
|
| 314 |
+
base_topics = ["python", "machine learning", "projects", "interview"]
|
| 315 |
+
rows = []
|
| 316 |
+
md_lines = [f"# {weeks}-Week Training Plan", f"**Goal:** {goal}", f"**Level:** {level}", f"**Time:** {hours_per_week} hours/week", ""]
|
| 317 |
+
for w in range(1, weeks + 1):
|
| 318 |
+
topic = base_topics[(w - 1) % len(base_topics)].title()
|
| 319 |
+
tasks = [
|
| 320 |
+
f"Learn/revise {topic} fundamentals",
|
| 321 |
+
f"Build one mini exercise related to {topic}",
|
| 322 |
+
"Write notes and create 5 flashcards",
|
| 323 |
+
"Practice 3 interview questions",
|
| 324 |
+
]
|
| 325 |
+
deliverable = f"Week {w} mini output: notes + exercise + interview answers"
|
| 326 |
+
rows.append({"Week": w, "Topic": topic, "Hours": hours_per_week, "Deliverable": deliverable})
|
| 327 |
+
md_lines.append(f"## Week {w}: {topic}\n- " + "\n- ".join(tasks) + f"\n- **Deliverable:** {deliverable}\n")
|
| 328 |
+
csv_path = write_csv("ashu_training_plan", rows)
|
| 329 |
+
pdf_path = write_pdf("ashu_training_plan", "ASHU Training Plan", [("Plan", "\n".join(md_lines))])
|
| 330 |
+
z = zip_files("ashu_training_plan", [csv_path, pdf_path])
|
| 331 |
+
return "\n".join(md_lines), z
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
def summarize_document(file_obj, pasted_text: str, style: str):
|
| 335 |
+
text = clean_text((pasted_text or "") + "\n" + read_file(file_obj))
|
| 336 |
+
if not text:
|
| 337 |
+
return "Upload/paste text first.", None
|
| 338 |
+
sents = re.split(r"(?<=[.!?])\s+", text)
|
| 339 |
+
words = keywords(text, 25)
|
| 340 |
+
selected = []
|
| 341 |
+
for s in sents:
|
| 342 |
+
score = sum(1 for w in words if w.lower() in s.lower())
|
| 343 |
+
if score > 0 and len(s.split()) > 6:
|
| 344 |
+
selected.append((score, s))
|
| 345 |
+
selected = [s for _, s in sorted(selected, reverse=True)[:8]]
|
| 346 |
+
if not selected:
|
| 347 |
+
selected = sents[:6]
|
| 348 |
+
if style == "Bullet summary":
|
| 349 |
+
md = "# Bullet Summary\n" + "\n".join(f"- {clean_text(s)}" for s in selected)
|
| 350 |
+
elif style == "Study notes":
|
| 351 |
+
md = "# Study Notes\n\n## Key Terms\n" + ", ".join(words[:15]) + "\n\n## Notes\n" + "\n".join(f"- {clean_text(s)}" for s in selected)
|
| 352 |
+
else:
|
| 353 |
+
md = "# Executive Summary\n\n" + " ".join(selected[:5]) + "\n\n## Key Terms\n" + ", ".join(words[:12])
|
| 354 |
+
path = write_pdf("ashu_summary", "ASHU Document Summary", [("Summary", md)])
|
| 355 |
+
return md, path
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
def flashcards_mcqs(file_obj, pasted_text: str, count: int):
|
| 359 |
+
text = clean_text((pasted_text or "") + "\n" + read_file(file_obj))
|
| 360 |
+
if not text:
|
| 361 |
+
return "Upload/paste text first.", None
|
| 362 |
+
kws = keywords(text, int(count) + 10)
|
| 363 |
+
rows = []
|
| 364 |
+
md = ["# Flashcards and MCQs"]
|
| 365 |
+
for i, k in enumerate(kws[:int(count)], 1):
|
| 366 |
+
rows.append({"type": "flashcard", "question": f"What is {k}?", "answer": f"Explain {k} using the provided material and add one example."})
|
| 367 |
+
rows.append({"type": "mcq", "question": f"Which statement best relates to {k}?", "answer": "Option A: Most relevant concept from the material."})
|
| 368 |
+
md.append(f"## {i}. {k.title()}\n**Flashcard:** What is {k}?\n\n**MCQ:** Which statement best relates to {k}?\nA. Most relevant concept from the material\nB. Unrelated term\nC. Random process\nD. None\n\n**Answer:** A\n")
|
| 369 |
+
csv_path = write_csv("ashu_flashcards_mcqs", rows)
|
| 370 |
+
return "\n".join(md), csv_path
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
def presenter_script(topic: str, audience: str, minutes: int):
|
| 374 |
+
topic = topic or "AI Mentor Platform"
|
| 375 |
+
audience = audience or "students and professionals"
|
| 376 |
+
minutes = int(minutes)
|
| 377 |
+
sections = max(3, min(8, minutes // 2 + 1))
|
| 378 |
+
md = [f"# Digital Presenter Script: {topic}", f"**Audience:** {audience}", f"**Duration:** {minutes} minutes", ""]
|
| 379 |
+
for i in range(1, sections + 1):
|
| 380 |
+
md.append(f"## Scene {i}: {['Hook','Problem','Solution','Workflow','Demo','Benefits','Closing','Call to Action'][i-1] if i <= 8 else 'Section'}")
|
| 381 |
+
md.append(f"Presenter says: Today we discuss **{topic}** for {audience}. In this section, explain the main idea clearly, show one example, and connect it to practical value.\n")
|
| 382 |
+
md.append(f"On-screen visual: simple title card, diagram, or bullet animation for scene {i}.\n")
|
| 383 |
+
md.append("## Closing\nThank you. The next step is to test the workflow, collect feedback, and improve the system iteratively.")
|
| 384 |
+
script = "\n".join(md)
|
| 385 |
+
path = write_text_file("ashu_presenter_script", script, "md")
|
| 386 |
+
return script, path
|
| 387 |
+
|
| 388 |
+
|
| 389 |
+
def candidate_scorecard(name: str, role: str, resume_file, resume_text: str, interview_notes: str):
|
| 390 |
+
text = clean_text((resume_text or "") + "\n" + read_file(resume_file) + "\n" + (interview_notes or ""))
|
| 391 |
+
found = detect_skills(text)
|
| 392 |
+
dimensions = {
|
| 393 |
+
"Technical fit": min(100, 30 + len(found) * 5),
|
| 394 |
+
"Communication": 75 if len((interview_notes or '').split()) > 60 else 55,
|
| 395 |
+
"Project depth": 80 if any(w in text.lower() for w in ["project", "built", "developed", "implemented"]) else 50,
|
| 396 |
+
"Role alignment": 85 if role and role.lower() in text.lower() else 60,
|
| 397 |
+
"Learning potential": 78,
|
| 398 |
+
}
|
| 399 |
+
final = round(sum(dimensions.values()) / len(dimensions), 1)
|
| 400 |
+
rows = [{"Dimension": k, "Score": v} for k, v in dimensions.items()]
|
| 401 |
+
md = f"# Candidate Scorecard\n\n**Name:** {name or 'Candidate'} \n**Role:** {role or 'Not specified'} \n**Final score:** **{final}/100**\n\n"
|
| 402 |
+
for k, v in dimensions.items():
|
| 403 |
+
md += f"- **{k}:** {v}/100\n"
|
| 404 |
+
md += "\n## Detected Strengths\n" + (", ".join(found[:20]) if found else "Add more evidence from resume/interview.")
|
| 405 |
+
md += "\n\n## Hiring Notes\nProceed if the candidate can explain project ownership, metrics, debugging, and deployment clearly."
|
| 406 |
+
csv_path = write_csv("ashu_candidate_scorecard", rows)
|
| 407 |
+
pdf_path = write_pdf("ashu_candidate_scorecard", "ASHU Candidate Scorecard", [("Scorecard", md)])
|
| 408 |
+
z = zip_files("ashu_candidate_scorecard", [csv_path, pdf_path])
|
| 409 |
+
return md, z
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
def architecture_diagram(system_name: str, features: str):
|
| 413 |
+
system_name = system_name or "ASHU AI Mentor Studio"
|
| 414 |
+
feature_list = [f.strip() for f in re.split(r"[,\n]", features or "") if f.strip()]
|
| 415 |
+
if not feature_list:
|
| 416 |
+
feature_list = ["Resume Analyzer", "Training Engine", "Interview Engine", "Report Generator", "Download Center"]
|
| 417 |
+
nodes = "\n".join([f" U --> F{i}[{f}]\n F{i} --> R[Reports and Downloads]" for i, f in enumerate(feature_list, 1)])
|
| 418 |
+
mermaid = f"""flowchart TD
|
| 419 |
+
U[User] --> UI[{system_name} UI]
|
| 420 |
+
UI --> P[Input Processing]
|
| 421 |
+
P --> K[Keyword and Skill Engine]
|
| 422 |
+
P --> S[Scoring Engine]
|
| 423 |
+
{nodes}
|
| 424 |
+
K --> R
|
| 425 |
+
S --> R
|
| 426 |
+
R --> D[PDF CSV ZIP Outputs]
|
| 427 |
+
"""
|
| 428 |
+
md = f"# Architecture Diagram\n\n```mermaid\n{mermaid}\n```\n\n## Components\n" + "\n".join(f"- {f}" for f in feature_list)
|
| 429 |
+
path = write_text_file("ashu_architecture_mermaid", mermaid, "mmd")
|
| 430 |
+
return md, path
|
| 431 |
+
|
| 432 |
+
|
| 433 |
+
def make_ui():
|
| 434 |
+
css = """
|
| 435 |
+
.gradio-container {max-width: 1280px !important}
|
| 436 |
+
#title-block {text-align:center; padding: 16px; border-radius: 16px; background: linear-gradient(90deg, #eef2ff, #f8fafc);}
|
| 437 |
+
"""
|
| 438 |
+
with gr.Blocks(title=APP_TITLE, css=css) as demo:
|
| 439 |
+
gr.Markdown(f"""
|
| 440 |
+
<div id='title-block'>
|
| 441 |
+
<h1>🎓 {APP_TITLE}</h1>
|
| 442 |
+
<p>Full-feature AI mentor app: resume analysis, ATS score, interview prep, training plan, summaries, quiz generation, presenter scripts, scorecards, and diagrams.</p>
|
| 443 |
+
</div>
|
| 444 |
+
""")
|
| 445 |
+
with gr.Tab("Resume + JD Analyzer"):
|
| 446 |
+
with gr.Row():
|
| 447 |
+
resume_file = gr.File(label="Upload Resume PDF/DOCX/TXT")
|
| 448 |
+
with gr.Column():
|
| 449 |
+
role = gr.Textbox(label="Target role", value="AI/ML Engineer")
|
| 450 |
+
jd = gr.Textbox(label="Paste Job Description", lines=10)
|
| 451 |
+
resume_text = gr.Textbox(label="Or paste resume text", lines=5)
|
| 452 |
+
btn = gr.Button("Analyze Resume", variant="primary")
|
| 453 |
+
out = gr.Markdown()
|
| 454 |
+
file_out = gr.File(label="Download report ZIP")
|
| 455 |
+
btn.click(ats_analyze, inputs=[resume_file, resume_text, jd, role], outputs=[out, file_out])
|
| 456 |
+
|
| 457 |
+
with gr.Tab("Cover Letter"):
|
| 458 |
+
with gr.Row():
|
| 459 |
+
cl_name = gr.Textbox(label="Candidate name")
|
| 460 |
+
cl_company = gr.Textbox(label="Company")
|
| 461 |
+
cl_role = gr.Textbox(label="Role")
|
| 462 |
+
cl_resume = gr.File(label="Upload resume")
|
| 463 |
+
cl_resume_text = gr.Textbox(label="Or paste resume text", lines=5)
|
| 464 |
+
cl_jd = gr.Textbox(label="Paste JD", lines=8)
|
| 465 |
+
cl_btn = gr.Button("Generate Cover Letter", variant="primary")
|
| 466 |
+
cl_out = gr.Textbox(label="Cover letter", lines=16)
|
| 467 |
+
cl_file = gr.File(label="Download TXT")
|
| 468 |
+
cl_btn.click(generate_cover_letter, [cl_name, cl_company, cl_role, cl_resume, cl_resume_text, cl_jd], [cl_out, cl_file])
|
| 469 |
+
|
| 470 |
+
with gr.Tab("Interview Preparation"):
|
| 471 |
+
with gr.Row():
|
| 472 |
+
iq_role = gr.Textbox(label="Role", value="AI/ML Engineer")
|
| 473 |
+
iq_skills = gr.Textbox(label="Skills comma-separated", value="Python, Machine Learning, SQL, Computer Vision")
|
| 474 |
+
iq_diff = gr.Dropdown(["Easy", "Medium", "Hard"], value="Medium", label="Difficulty")
|
| 475 |
+
iq_count = gr.Slider(5, 30, value=10, step=1, label="Number of questions")
|
| 476 |
+
iq_btn = gr.Button("Generate Questions", variant="primary")
|
| 477 |
+
iq_out = gr.Markdown()
|
| 478 |
+
iq_file = gr.File(label="Download CSV")
|
| 479 |
+
iq_btn.click(interview_questions, [iq_role, iq_skills, iq_diff, iq_count], [iq_out, iq_file])
|
| 480 |
+
|
| 481 |
+
gr.Markdown("## Answer Scoring")
|
| 482 |
+
q_in = gr.Textbox(label="Interview question", lines=3)
|
| 483 |
+
a_in = gr.Textbox(label="Candidate answer", lines=8)
|
| 484 |
+
score_btn = gr.Button("Score Answer")
|
| 485 |
+
score_out = gr.Markdown()
|
| 486 |
+
score_btn.click(score_answer, [q_in, a_in], score_out)
|
| 487 |
+
|
| 488 |
+
with gr.Tab("Training Engine"):
|
| 489 |
+
with gr.Row():
|
| 490 |
+
goal = gr.Textbox(label="Training goal", value="Become job-ready in AI, LLMs, RAG, Python and interview preparation")
|
| 491 |
+
level = gr.Dropdown(["Beginner", "Intermediate", "Advanced"], value="Intermediate", label="Level")
|
| 492 |
+
weeks = gr.Slider(2, 24, value=8, step=1, label="Weeks")
|
| 493 |
+
hpw = gr.Slider(2, 20, value=6, step=1, label="Hours/week")
|
| 494 |
+
tr_btn = gr.Button("Generate Training Plan", variant="primary")
|
| 495 |
+
tr_out = gr.Markdown()
|
| 496 |
+
tr_file = gr.File(label="Download ZIP")
|
| 497 |
+
tr_btn.click(training_plan, [goal, level, weeks, hpw], [tr_out, tr_file])
|
| 498 |
+
|
| 499 |
+
with gr.Tab("Document Summarizer"):
|
| 500 |
+
doc_file = gr.File(label="Upload PDF/DOCX/TXT")
|
| 501 |
+
doc_text = gr.Textbox(label="Or paste document text", lines=10)
|
| 502 |
+
style = gr.Dropdown(["Executive summary", "Bullet summary", "Study notes"], value="Bullet summary", label="Summary style")
|
| 503 |
+
sum_btn = gr.Button("Summarize", variant="primary")
|
| 504 |
+
sum_out = gr.Markdown()
|
| 505 |
+
sum_file = gr.File(label="Download PDF/TXT")
|
| 506 |
+
sum_btn.click(summarize_document, [doc_file, doc_text, style], [sum_out, sum_file])
|
| 507 |
+
|
| 508 |
+
with gr.Tab("Flashcards + MCQs"):
|
| 509 |
+
fc_file = gr.File(label="Upload PDF/DOCX/TXT")
|
| 510 |
+
fc_text = gr.Textbox(label="Or paste text", lines=10)
|
| 511 |
+
fc_count = gr.Slider(5, 40, value=12, step=1, label="Number of items")
|
| 512 |
+
fc_btn = gr.Button("Generate Flashcards + MCQs", variant="primary")
|
| 513 |
+
fc_out = gr.Markdown()
|
| 514 |
+
fc_file_out = gr.File(label="Download CSV")
|
| 515 |
+
fc_btn.click(flashcards_mcqs, [fc_file, fc_text, fc_count], [fc_out, fc_file_out])
|
| 516 |
+
|
| 517 |
+
with gr.Tab("Digital Presenter"):
|
| 518 |
+
pr_topic = gr.Textbox(label="Topic", value="ASHU AI Mentor Platform")
|
| 519 |
+
pr_aud = gr.Textbox(label="Audience", value="students, recruiters, and training teams")
|
| 520 |
+
pr_min = gr.Slider(2, 20, value=6, step=1, label="Minutes")
|
| 521 |
+
pr_btn = gr.Button("Generate Presenter Script", variant="primary")
|
| 522 |
+
pr_out = gr.Markdown()
|
| 523 |
+
pr_file = gr.File(label="Download Markdown")
|
| 524 |
+
pr_btn.click(presenter_script, [pr_topic, pr_aud, pr_min], [pr_out, pr_file])
|
| 525 |
+
|
| 526 |
+
with gr.Tab("Candidate Scorecard"):
|
| 527 |
+
with gr.Row():
|
| 528 |
+
cand_name = gr.Textbox(label="Candidate name")
|
| 529 |
+
cand_role = gr.Textbox(label="Role", value="AI/ML Engineer")
|
| 530 |
+
cand_file = gr.File(label="Upload resume")
|
| 531 |
+
cand_text = gr.Textbox(label="Or paste resume text", lines=5)
|
| 532 |
+
cand_notes = gr.Textbox(label="Interview notes / answer transcript", lines=8)
|
| 533 |
+
cand_btn = gr.Button("Create Scorecard", variant="primary")
|
| 534 |
+
cand_out = gr.Markdown()
|
| 535 |
+
cand_zip = gr.File(label="Download ZIP")
|
| 536 |
+
cand_btn.click(candidate_scorecard, [cand_name, cand_role, cand_file, cand_text, cand_notes], [cand_out, cand_zip])
|
| 537 |
+
|
| 538 |
+
with gr.Tab("Architecture Diagrams"):
|
| 539 |
+
sys_name = gr.Textbox(label="System name", value="ASHU AI Mentor Studio")
|
| 540 |
+
features = gr.Textbox(label="Features, one per line", value="Resume Analyzer\nATS Score Engine\nTraining Engine\nInterview Engine\nDocument Summarizer\nReport Generator", lines=8)
|
| 541 |
+
arch_btn = gr.Button("Generate Mermaid Architecture", variant="primary")
|
| 542 |
+
arch_out = gr.Markdown()
|
| 543 |
+
arch_file = gr.File(label="Download .mmd")
|
| 544 |
+
arch_btn.click(architecture_diagram, [sys_name, features], [arch_out, arch_file])
|
| 545 |
+
|
| 546 |
+
with gr.Tab("Health Check"):
|
| 547 |
+
gr.Markdown(f"""
|
| 548 |
+
# ✅ Health Check
|
| 549 |
+
|
| 550 |
+
- App type: **Native Gradio Space**
|
| 551 |
+
- Heavy GPU modules: **disabled**
|
| 552 |
+
- Docker: **not required**
|
| 553 |
+
- External APIs: **not required**
|
| 554 |
+
- Output folder: `{OUT_DIR}`
|
| 555 |
+
|
| 556 |
+
If you can see this page, ASHU is running correctly.
|
| 557 |
+
""")
|
| 558 |
+
return demo
|
| 559 |
+
|
| 560 |
+
|
| 561 |
+
demo = make_ui()
|
| 562 |
+
|
| 563 |
+
if __name__ == "__main__":
|
| 564 |
+
demo.launch()
|
assets/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
keep
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio==4.44.1
|
| 2 |
+
pandas==2.2.2
|
| 3 |
+
numpy==1.26.4
|
| 4 |
+
PyPDF2==3.0.1
|
| 5 |
+
python-docx==1.1.2
|
| 6 |
+
reportlab==4.2.2
|
sample_data/training_catalog.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"AI/ML": ["Machine Learning", "Deep Learning", "Computer Vision", "NLP", "LLMs", "RAG", "MLOps", "Model Evaluation", "Prompt Engineering", "Responsible AI"],
|
| 3 |
+
"Data": ["Python", "SQL", "Pandas", "Data Cleaning", "Feature Engineering", "EDA", "Dashboards", "Statistics", "Experiment Design"],
|
| 4 |
+
"Software": ["APIs", "Microservices", "Git", "Docker", "Testing", "System Design", "Cloud Basics", "Security Basics"],
|
| 5 |
+
"Career": ["Resume Writing", "Interview Preparation", "Communication", "Leadership", "Presentation", "Research Writing"]
|
| 6 |
+
}
|