Spaces:
Runtime error
Runtime error
| import os | |
| # Disable experimental Gradio Node.js SSR proxy on Hugging Face Spaces | |
| os.environ["GRADIO_SSR_MODE"] = "false" | |
| os.environ["GRADIO_SERVER_NAME"] = "0.0.0.0" | |
| os.environ["GRADIO_SERVER_PORT"] = "7860" | |
| import sys | |
| import json | |
| import gradio as gr | |
| import pandas as pd | |
| import plotly.express as px | |
| # Ensure tech_radar package is importable | |
| sys.path.insert(0, os.path.abspath(".")) | |
| from tech_radar.db.database import DatabaseManager | |
| from tech_radar.db.vector_store import SemanticVectorStore | |
| from tech_radar.agents.evaluator_agent import EvaluatorAgent | |
| from tech_radar.agents.market_analyst import MarketAnalystAgent | |
| from tech_radar.scrapers.seeder import seed_database | |
| from tech_radar.mcp.tools import ( | |
| tool_search_tech_jobs, | |
| tool_analyze_skill_gap, | |
| tool_generate_resume_patch, | |
| tool_get_market_insights, | |
| tool_generate_interview_prep_kit | |
| ) | |
| # Initialize DB & Vector Engine | |
| db, vector_store = seed_database(db_path="tech_radar.db") | |
| evaluator = EvaluatorAgent() | |
| market_analyst = MarketAnalystAgent(db) | |
| # Load 3D Cyber Web App HTML | |
| web_html_path = os.path.join("tech_radar", "ui", "web_app.html") | |
| web_html_content = "" | |
| if os.path.exists(web_html_path): | |
| with open(web_html_path, "r", encoding="utf-8") as f: | |
| web_html_content = f.read() | |
| # Gradio Callbacks | |
| def search_jobs_fn(domain, city, query, min_exp): | |
| if query: | |
| semantic_results = vector_store.search_semantic(query=query, domain=domain, city=city, top_k=30) | |
| jobs_list = [j for j, score in semantic_results] | |
| else: | |
| all_jobs = db.search_jobs(domain=domain, city=city, limit=200) | |
| jobs_list = [j for j in all_jobs if j.experience_min_years <= min_exp] | |
| data = [] | |
| for j in jobs_list: | |
| data.append({ | |
| "Job ID": j.id, | |
| "Title": j.title, | |
| "Company": j.company, | |
| "Domain": j.tech_domain, | |
| "City": j.city, | |
| "Salary (LPA)": f"₹{j.salary_min_lpa}L - ₹{j.salary_max_lpa}L", | |
| "Exp": f"{j.experience_min_years}-{j.experience_max_years} Yrs", | |
| "Work Mode": j.work_mode, | |
| "Tech Stack": ", ".join(j.tech_stack) | |
| }) | |
| return pd.DataFrame(data) | |
| def market_analytics_fn(domain, city): | |
| insights = market_analyst.generate_market_report(city=city, domain=domain) | |
| df_frameworks = pd.DataFrame(insights.top_demanded_frameworks) | |
| if not df_frameworks.empty: | |
| fig = px.bar( | |
| df_frameworks, | |
| x="percentage", | |
| y="skill", | |
| orientation="h", | |
| title=f"Top Skill Demand % in {city} ({domain})", | |
| labels={"percentage": "Demand %", "skill": "Framework"}, | |
| color="percentage", | |
| color_continuous_scale="Purples" | |
| ) | |
| fig.update_layout(template="plotly_dark", yaxis={'categoryorder':'total ascending'}) | |
| else: | |
| fig = px.bar(title="No data available") | |
| summary_md = f""" | |
| ### 📊 Hiring Summary for {city} ({domain}) | |
| - **Total Active Jobs Tracked**: `{insights.total_active_jobs}` | |
| - **Average Salary**: `{insights.avg_salary_lpa} LPA` | |
| - **Salary Range**: `{insights.salary_range}` | |
| - **Market Trend**: {insights.growth_trend} | |
| **Top Employers Hiring**: {', '.join(insights.top_employers)} | |
| """ | |
| return summary_md, fig | |
| def evaluate_ats_fn(resume_text, job_id): | |
| job = db.get_job_by_id(job_id) | |
| if not job: | |
| return "Job ID not found", "", "", "", "" | |
| report = evaluator.evaluate_skill_gap(resume_text=resume_text, candidate_skills=[], job=job) | |
| patch = evaluator.generate_resume_patch(resume_text=resume_text, job=job) | |
| prep = evaluator.generate_interview_prep(job=job) | |
| matched_str = ", ".join(report.matched_skills) or "None" | |
| missing_str = ", ".join(report.missing_skills) or "None" | |
| bullets_md = "" | |
| for b in patch.tailored_bullets: | |
| bullets_md += f"- **Original**: {b['original']}\n - **Tailored**: `{b['tailored']}`\n" | |
| prep_md = "" | |
| for q in prep.technical_questions: | |
| prep_md += f"**Q ({q.category})**: {q.question}\n- *Key Answer Points*: {', '.join(q.ideal_answer_points)}\n\n" | |
| return f"{report.match_percentage}%", matched_str, missing_str, bullets_md, prep_md | |
| def execute_mcp_tool_fn(tool_name, domain, city, query, job_id, resume_text): | |
| if tool_name == "search_tech_jobs": | |
| res = tool_search_tech_jobs(domain=domain, city=city, query=query) | |
| elif tool_name == "analyze_skill_gap": | |
| res = tool_analyze_skill_gap(resume_text=resume_text, target_job_id=job_id) | |
| elif tool_name == "generate_tailored_resume_patch": | |
| res = tool_generate_resume_patch(resume_text=resume_text, target_job_id=job_id) | |
| elif tool_name == "get_market_insights": | |
| res = tool_get_market_insights(domain=domain, city=city) | |
| elif tool_name == "generate_interview_prep_kit": | |
| res = tool_generate_interview_prep_kit(target_job_id=job_id) | |
| else: | |
| res = json.dumps({"error": "Unknown tool"}) | |
| try: | |
| return json.dumps(json.loads(res), indent=2) | |
| except: | |
| return res | |
| # Build Gradio UI | |
| theme = gr.themes.Soft( | |
| primary_hue="cyan", | |
| secondary_hue="purple", | |
| neutral_hue="slate" | |
| ) | |
| with gr.Blocks(theme=theme, title="TechRadar MCP — Universal Tech Hiring Intelligence") as demo: | |
| gr.Markdown(""" | |
| # ⚡ TechRadar MCP — Universal Tech Hiring Intelligence | |
| ### Autonomous Model Context Protocol Ecosystem across Indian Tech Hubs & Remote | |
| """) | |
| with gr.Tabs(): | |
| with gr.TabItem("📡 Universal Tech Job Radar"): | |
| with gr.Row(): | |
| domain_dropdown = gr.Dropdown(["All", "Backend Engineering", "Frontend Engineering", "Full Stack Engineering", "Cloud & DevOps", "Data Engineering", "AI/ML & GenAI", "Mobile Engineering"], value="All", label="Tech Domain") | |
| city_dropdown = gr.Dropdown(["All", "Bengaluru", "Pune", "Hyderabad", "Gurgaon", "Mumbai", "Chennai", "Remote"], value="All", label="City / Region") | |
| query_input = gr.Textbox(placeholder="Search Go, React, vLLM...", label="Semantic Search") | |
| exp_slider = gr.Slider(0, 12, value=10, step=1, label="Max Experience (Yrs)") | |
| search_btn = gr.Button("🔍 Search Tech Jobs", variant="primary") | |
| job_table = gr.Dataframe(label="Matching Active Roles") | |
| search_btn.click( | |
| fn=search_jobs_fn, | |
| inputs=[domain_dropdown, city_dropdown, query_input, exp_slider], | |
| outputs=job_table | |
| ) | |
| with gr.TabItem("📊 Market Analytics"): | |
| with gr.Row(): | |
| an_domain = gr.Dropdown(["All", "Backend Engineering", "Frontend Engineering", "Cloud & DevOps", "AI/ML & GenAI"], value="All", label="Domain") | |
| an_city = gr.Dropdown(["All", "Bengaluru", "Pune", "Hyderabad", "Gurgaon", "Remote"], value="All", label="City") | |
| an_btn = gr.Button("📊 Generate Market Report", variant="primary") | |
| with gr.Row(): | |
| an_summary = gr.Markdown() | |
| an_chart = gr.Plot() | |
| an_btn.click( | |
| fn=market_analytics_fn, | |
| inputs=[an_domain, an_city], | |
| outputs=[an_summary, an_chart] | |
| ) | |
| with gr.TabItem("🎯 ATS Resume & Skill Gap Evaluator"): | |
| all_jobs = db.get_all_jobs() | |
| job_ids = [j.id for j in all_jobs] | |
| with gr.Row(): | |
| with gr.Column(): | |
| resume_input = gr.Textbox( | |
| value="Senior Backend Engineer with 4 years experience building Go microservices, REST APIs, Docker, and PostgreSQL in Bengaluru.", | |
| lines=8, | |
| label="Paste Candidate Resume Text" | |
| ) | |
| job_id_select = gr.Dropdown(job_ids, value=job_ids[0] if job_ids else "", label="Select Target Job ID") | |
| eval_btn = gr.Button("🚀 Analyze ATS Match & Skill Gap", variant="primary") | |
| with gr.Column(): | |
| match_score_out = gr.Textbox(label="Match Percentage") | |
| matched_skills_out = gr.Textbox(label="Matched Skills") | |
| missing_skills_out = gr.Textbox(label="Missing Dealbreaker Skills") | |
| bullets_out = gr.Markdown(label="ATS Tailored Resume Bullets") | |
| prep_out = gr.Markdown(label="Technical Interview Q&A Prep") | |
| eval_btn.click( | |
| fn=evaluate_ats_fn, | |
| inputs=[resume_input, job_id_select], | |
| outputs=[match_score_out, matched_skills_out, missing_skills_out, bullets_out, prep_out] | |
| ) | |
| with gr.TabItem("🧪 FastMCP Server Tester"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| tool_select = gr.Dropdown(["search_tech_jobs", "analyze_skill_gap", "generate_tailored_resume_patch", "get_market_insights", "generate_interview_prep_kit"], value="search_tech_jobs", label="Select MCP Tool") | |
| mcp_domain = gr.Textbox(value="Backend Engineering", label="domain (if search/insights)") | |
| mcp_city = gr.Textbox(value="Bengaluru", label="city (if search/insights)") | |
| mcp_query = gr.Textbox(value="Go Distributed Systems", label="query (if search)") | |
| mcp_job_id = gr.Textbox(value=job_ids[0] if job_ids else "BLR-BACKEND-101", label="target_job_id") | |
| mcp_resume = gr.Textbox(value="Go & Docker developer", label="resume_text") | |
| exec_mcp_btn = gr.Button("⚡ Execute FastMCP Tool", variant="primary") | |
| with gr.Column(): | |
| mcp_output_json = gr.Code(language="json", label="JSON-RPC Tool Response") | |
| exec_mcp_btn.click( | |
| fn=execute_mcp_tool_fn, | |
| inputs=[tool_select, mcp_domain, mcp_city, mcp_query, mcp_job_id, mcp_resume], | |
| outputs=mcp_output_json | |
| ) | |
| # Launch Gradio App | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860) | |