diff --git "a/Notebooks/CodeForge.ipynb" "b/Notebooks/CodeForge.ipynb" --- "a/Notebooks/CodeForge.ipynb" +++ "b/Notebooks/CodeForge.ipynb" @@ -2,21 +2,10 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "e02e1b00", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import os\n", "import cloudinary\n", @@ -38,18 +27,10 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "c11377c5", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "❌ Upload failed: [Errno 2] No such file or directory: 'c:\\\\Users\\\\ATHARVA\\\\Downloads\\\\my codes\\\\python\\\\machine_learning\\\\Learning_Files\\\\ChirayuResume.pdf'\n" - ] - } - ], + "outputs": [], "source": [ "import os\n", "import cloudinary\n", @@ -93,18 +74,10 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "f986ff8f", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✅ PDF loaded into memory for processing!\n" - ] - } - ], + "outputs": [], "source": [ "import requests\n", "from io import BytesIO\n", @@ -129,7 +102,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "938186bf", "metadata": {}, "outputs": [], @@ -161,36 +134,10 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "f4340cbb", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found resume: https://res.cloudinary.com/dvxnazx8e/image/upload/v1774166452/threads/trial_thread_001/ChirayuResume.pdf\n", - "URL: https://res.cloudinary.com/dvxnazx8e/image/upload/v1774166452/threads/trial_thread_001/ChirayuResume.pdf\n", - "Pages loaded: 1\n", - "Preview:\n", - "Chirayu Jain\n", - "SOFTWARE DEVELOPER\n", - "+91-9462128878 | chirayujain93@gmail.com | LinkedIn | GitHub | CodeChef | Leetcode\n", - "EDUCATION\n", - "Indian Institute of Information Technology, Kota (CGPA-8.1)\n", - "Kota, Rajasthan\n", - "B.Tech in Electronics and Communication Engineering\n", - "2022 - Present\n", - "WORK EXPERIENCE\n", - "NAV India\n", - "Jan 2026 - Present\n", - "Technology Intern\n", - "Onsite – Jaipur, Rajasthan\n", - "• As part of the AI/ML team, built Flask API for the RTA investor verification team, implementing 2\n", - "confidence-scoring pipelines using OCR ext\n" - ] - } - ], + "outputs": [], "source": [ "import requests\n", "import tempfile\n", @@ -232,7 +179,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "b010e49b", "metadata": {}, "outputs": [], @@ -377,7 +324,7 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": 2, "id": "7da5b1c6", "metadata": {}, "outputs": [], @@ -506,7 +453,7 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": 3, "id": "4b2441cd", "metadata": {}, "outputs": [], @@ -619,7 +566,7 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": 4, "id": "4f1341e0", "metadata": {}, "outputs": [], @@ -679,7 +626,7 @@ }, { "cell_type": "code", - "execution_count": 194, + "execution_count": 5, "id": "18663bb3", "metadata": {}, "outputs": [], @@ -716,7 +663,7 @@ }, { "cell_type": "code", - "execution_count": 172, + "execution_count": 6, "id": "14dab004", "metadata": {}, "outputs": [], @@ -746,7 +693,7 @@ }, { "cell_type": "code", - "execution_count": 173, + "execution_count": 7, "id": "472dae2f", "metadata": {}, "outputs": [], @@ -776,7 +723,7 @@ }, { "cell_type": "code", - "execution_count": 174, + "execution_count": 8, "id": "0d5e3b17", "metadata": {}, "outputs": [], @@ -808,7 +755,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 9, "id": "0ccc026b", "metadata": {}, "outputs": [], @@ -829,7 +776,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 10, "id": "c8827093", "metadata": {}, "outputs": [ @@ -840,12 +787,12 @@ "Index ready: {'_response_info': {'raw_headers': {'connection': 'keep-alive',\n", " 'content-length': '187',\n", " 'content-type': 'application/json',\n", - " 'date': 'Sun, 22 Mar 2026 17:18:29 GMT',\n", + " 'date': 'Mon, 23 Mar 2026 20:11:40 GMT',\n", " 'grpc-status': '0',\n", " 'server': 'envoy',\n", - " 'x-envoy-upstream-service-time': '65',\n", - " 'x-pinecone-request-latency-ms': '64',\n", - " 'x-pinecone-response-duration-ms': '68'}},\n", + " 'x-envoy-upstream-service-time': '62',\n", + " 'x-pinecone-request-latency-ms': '61',\n", + " 'x-pinecone-response-duration-ms': '64'}},\n", " 'dimension': 384,\n", " 'index_fullness': 0.0,\n", " 'memoryFullness': 0.0,\n", @@ -893,7 +840,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 12, "id": "7561b3a1", "metadata": {}, "outputs": [], @@ -901,11 +848,14 @@ "import json\n", "from langchain_core.documents import Document\n", "\n", - "# Initialize the list\n", + "\n", + "doc_path=r\"C:\\Users\\ATHARVA\\Downloads\\my codes\\web\\AdaptiveEngineService\\app\\utils\\langchain_formatted.json\"\n", + "\n", + "\n", "documents = []\n", "\n", "# Load the transformed catalog\n", - "with open(\"formatted_catalog.json\", \"r\") as f:\n", + "with open(doc_path, \"r\") as f:\n", " data = json.load(f)\n", " for doc in data:\n", " # Create a LangChain Document object for each entry\n", @@ -919,7 +869,7 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 13, "id": "f0845a99", "metadata": {}, "outputs": [], @@ -929,14 +879,22 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 14, "id": "c8e6d2a5", "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\ATHARVA\\AppData\\Local\\Temp\\ipykernel_30068\\2526755923.py:1: LangChainDeprecationWarning: The class `HuggingFaceEmbeddings` was deprecated in LangChain 0.2.2 and will be removed in 1.0. An updated version of the class exists in the `langchain-huggingface package and should be used instead. To use it run `pip install -U `langchain-huggingface` and import as `from `langchain_huggingface import HuggingFaceEmbeddings``.\n", + " embeddings = HuggingFaceEmbeddings(model_name=\"sentence-transformers/all-MiniLM-L6-v2\", model_kwargs={\"device\": device})\n" + ] + }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "fd15feaccf43437ebae846046396208c", + "model_id": "3af10b64d4584d53952822157482186f", "version_major": 2, "version_minor": 0 }, @@ -967,23 +925,18 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "6bc7292f", "metadata": {}, "outputs": [ { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "2324bc9377764bdea82e657fac5b74a9", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - " 0%| | 0/33 [00:00log for further details." + ] } ], "source": [ @@ -994,66 +947,69 @@ "retriever = PineconeHybridSearchRetriever(\n", " embeddings=embeddings,\n", " sparse_encoder=bm25_encoder,\n", - " index=index\n", + " index=index,\n", + " alpha=0.5\n", ")" ] }, { "cell_type": "code", - "execution_count": 175, + "execution_count": null, "id": "03c755a1", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + } + ], "source": [ "from langchain_core.tools import tool\n", "from typing import Optional\n", "\n", "@tool\n", - "def search_courses(query: str, level: str, category: str):\n", + "def search_courses(query: str):\n", " \"\"\"\n", - " Search the course catalog for relevant modules based on a skill query, \n", - " difficulty level, and technical category.\n", + " Search the course catalog for relevant modules based on a skill query \n", + " \n", " \n", " Args:\n", - " query: The semantic search term (e.g., 'FastAPI', 'PostgreSQL', 'Docker').\n", - " level: The difficulty level required ('beginner', 'intermediate', or 'strong').\n", - " category: The technical domain ('Backend', 'Frontend', 'DevOps', 'Cybersecurity', 'Database', 'ML').\n", + " \n", + " query:the skill to find with semantic terms (e.g., 'FastAPI', 'PostgreSQL', 'Docker','Enterprise VMS Strategy','Utilization Management').\n", + " \n", " \"\"\"\n", " \n", - " # Using the hybrid search logic you perfected\n", - " # The '$and' ensures the agent gets EXACTLY what fits the candidate's level\n", " results = retriever.invoke(\n", - " query, \n", - " filter={\n", - " \"$and\": [\n", - " {\"level\": level},\n", - " {\"category\": category}\n", - " ]\n", - " }\n", + " query\n", " )\n", "\n", " if not results:\n", - " return f\"No {level} level courses found in the {category} category for '{query}'.\"\n", + " return f\"No courses found for '{query}'.\"\n", "\n", - " # Format the output so the Agent can read the metadata easily\n", " formatted_output = []\n", " for doc in results:\n", - " course_info = (\n", - " f\"ID: {doc.metadata.get('course_id')}\\n\"\n", - " f\"Title: {doc.metadata.get('title')}\\n\"\n", - " f\"Description: {doc.page_content}\\n\"\n", - " f\"Prerequisites: {doc.metadata.get('prerequisites')}\\n\"\n", - " f\"Duration: {doc.metadata.get('duration')} hours\\n\"\n", + " course_id = doc.metadata.get('course_id', 'N/A')\n", + " \n", + " # We include the ID for roadmap generation, followed by the full context\n", + " # created during the transformation stage (Title, Desc, Outcomes, Prereqs).\n", + " course_block = (\n", + " f\"ID: {course_id}\\n\"\n", + " f\"{doc.page_content}\\n\"\n", " \"---\"\n", " )\n", - " formatted_output.append(course_info)\n", + " formatted_output.append(course_block)\n", "\n", " return \"\\n\".join(formatted_output)" ] }, { "cell_type": "code", - "execution_count": 176, + "execution_count": 16, "id": "9db28710", "metadata": {}, "outputs": [], @@ -1085,7 +1041,7 @@ " return self.courses_map.get(course_id)\n", "\n", "\n", - "lookup_service = CourseLookup(\"Catalog.json\")\n", + "lookup_service = CourseLookup(r\"C:\\Users\\ATHARVA\\Downloads\\my codes\\web\\AdaptiveEngineService\\app\\tools\\Catalog.json\")\n", "\n", "@tool\n", "def get_course_by_id(course_id: str) -> str:\n", @@ -1104,7 +1060,7 @@ }, { "cell_type": "code", - "execution_count": 198, + "execution_count": 18, "id": "09d238ab", "metadata": {}, "outputs": [], @@ -1137,7 +1093,7 @@ }, { "cell_type": "code", - "execution_count": 165, + "execution_count": null, "id": "6ad04bc6", "metadata": {}, "outputs": [], @@ -1154,12 +1110,12 @@ " # 2. Now this assignment updates the global variable\n", " mermaid_roadmap_code = mermaid_code\n", " \n", - " return mermaid_code" + " return \"Mermaid visualization saved successfully.\"" ] }, { "cell_type": "code", - "execution_count": 199, + "execution_count": null, "id": "285f74bb", "metadata": {}, "outputs": [], @@ -1169,7 +1125,7 @@ }, { "cell_type": "code", - "execution_count": 200, + "execution_count": null, "id": "47564782", "metadata": {}, "outputs": [], @@ -1177,6 +1133,17 @@ "roadmap_planner_agent=roadmap_planner_agent.bind_tools(roadmap_planner_agent_tools)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "c311f642", + "metadata": {}, + "outputs": [], + "source": [ + "# Replace 'roadmap_planner_agent' with your bound model variable\n", + "print(roadmap_planner_agent.kwargs.get(\"tools\"))\n" + ] + }, { "cell_type": "markdown", "id": "2da3f43b", @@ -1187,7 +1154,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "id": "7cfbfc3a", "metadata": {}, "outputs": [], @@ -1205,7 +1172,7 @@ }, { "cell_type": "code", - "execution_count": 201, + "execution_count": null, "id": "5deda2bb", "metadata": {}, "outputs": [], @@ -1238,7 +1205,7 @@ }, { "cell_type": "code", - "execution_count": 202, + "execution_count": null, "id": "c8df9934", "metadata": {}, "outputs": [], @@ -1275,7 +1242,7 @@ }, { "cell_type": "code", - "execution_count": 203, + "execution_count": null, "id": "608efafd", "metadata": {}, "outputs": [], @@ -1315,7 +1282,7 @@ }, { "cell_type": "code", - "execution_count": 204, + "execution_count": null, "id": "8a6c1483", "metadata": {}, "outputs": [], @@ -1352,7 +1319,7 @@ }, { "cell_type": "code", - "execution_count": 205, + "execution_count": null, "id": "059e5f86", "metadata": {}, "outputs": [], @@ -1365,35 +1332,25 @@ "\n", "\n", "STEP 1 — SEARCH\n", - "For every high/medium priority gap → call search_courses.\n", + "For every gap → call search_courses.\n", "Use ONLY course IDs returned by the tool. Never guess IDs.\n", "\n", "STEP 2 — RESOLVE PREREQUISITES\n", "For each retrieved course inspect its prerequisites list.\n", "If candidate's resume does NOT prove mastery → call get_course_by_id for each missing prerequisite.\n", - "Repeat recursively until full dependency chain is resolved.\n", - "\n", - "STEP 3 — PRUNE\n", "Skip courses the candidate already demonstrates via projects or experience.\n", - "Cap roadmap at 5-6 modules max.\n", "\n", - "STEP 4 — SEQUENCE\n", + "STEP 3 — SEQUENCE\n", "Prerequisites always before target modules.\n", "sequence_order must be 1, 2, 3... strictly.\n", "If is_fresher_adaptation_needed is True → add a professional fundamentals module first.\n", "\n", - "STEP 5 — SUBMIT (TERMINAL STEP)\n", + "STEP 4 — SUBMIT (TERMINAL STEP)\n", "Call submit_final_roadmap ONCE with the complete roadmap.\n", "Call submit_mermaid_visualization ONCE with the Mermaid string.\n", "After both return → STOP. Do not call any tool again.\n", "\n", "\n", - "\n", - "- Use candidate's actual name — never use placeholder names.\n", - "- Never write JSON or describe what you will submit — just call the tools.\n", - "- Never call submit tools more than once each.\n", - "\n", - "\n", "\n", "- gap courses → :::gap\n", "- known prerequisites → :::known\n", @@ -1426,7 +1383,7 @@ }, { "cell_type": "code", - "execution_count": 206, + "execution_count": null, "id": "9c4dea1e", "metadata": {}, "outputs": [], @@ -1465,7 +1422,7 @@ }, { "cell_type": "code", - "execution_count": 207, + "execution_count": null, "id": "eb13ffc0", "metadata": {}, "outputs": [], @@ -1503,7 +1460,7 @@ }, { "cell_type": "code", - "execution_count": 208, + "execution_count": null, "id": "330acef6", "metadata": {}, "outputs": [], @@ -1558,7 +1515,7 @@ }, { "cell_type": "code", - "execution_count": 209, + "execution_count": null, "id": "7352181c", "metadata": {}, "outputs": [], @@ -1616,7 +1573,7 @@ }, { "cell_type": "code", - "execution_count": 210, + "execution_count": null, "id": "1fb2f0d1", "metadata": {}, "outputs": [], @@ -1640,7 +1597,7 @@ }, { "cell_type": "code", - "execution_count": 211, + "execution_count": null, "id": "cea90664", "metadata": {}, "outputs": [], @@ -1681,7 +1638,7 @@ }, { "cell_type": "code", - "execution_count": 212, + "execution_count": null, "id": "ba9f22e1", "metadata": {}, "outputs": [], @@ -1691,7 +1648,7 @@ }, { "cell_type": "code", - "execution_count": 213, + "execution_count": null, "id": "b5cfe4c3", "metadata": {}, "outputs": [], @@ -1740,28 +1697,17 @@ }, { "cell_type": "code", - "execution_count": 190, + "execution_count": null, "id": "53588a77", "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "display(graph)" ] }, { "cell_type": "code", - "execution_count": 214, + "execution_count": null, "id": "0edf8d59", "metadata": {}, "outputs": [], @@ -1790,7 +1736,7 @@ }, { "cell_type": "code", - "execution_count": 215, + "execution_count": null, "id": "da3df5a4", "metadata": {}, "outputs": [], @@ -1831,25 +1777,10 @@ }, { "cell_type": "code", - "execution_count": 216, + "execution_count": null, "id": "a95b4db7", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "📂 File path received: C:\\Users\\ATHARVA\\Downloads\\my codes\\web\\AdaptiveEngineService\\AI_Engine_Evaluation\\Testcases\\Testresume\\ChirayuResume.pdf\n", - "📂 File exists: True\n", - "📄 Pages loaded: 1\n", - "📄 Text length: 3156\n", - "DEBUGGER: Sending 525 characters to JD Agent...\n", - "📄 Resume text length: 3156 chars\n", - "DEBUGGER SUCCESS: Extracted Backend Developer\n", - "✅ Resume extracted: Software Developer\n" - ] - } - ], + "outputs": [], "source": [ "initial_input = {\n", " \"candidate_name\": \"Chirayu Jain\",\n", @@ -1881,91 +1812,107 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": null, + "id": "093bdd6e", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "\n", + "def run_graph_with_stream(graph, initial_input, config):\n", + " \"\"\"\n", + " Executes the graph in streaming mode to visualize the 'under the hood' \n", + " process of node transitions and data updates.\n", + " \"\"\"\n", + " print(\"🚀 Starting Graph Stream...\\n\")\n", + " \n", + " # Using stream_mode=\"updates\" to see exactly what each node returns\n", + " for event in graph.stream(initial_input, config, stream_mode=\"updates\"):\n", + " for node_name, node_update in event.items():\n", + " print(f\"--- 📍 Node: {node_name} ---\")\n", + " \n", + " # 1. Check for Tool Calls (The 'ReAct' thinking process)\n", + " if \"messages\" in node_update:\n", + " last_msg = node_update[\"messages\"][-1]\n", + " if hasattr(last_msg, \"tool_calls\") and last_msg.tool_calls:\n", + " for tool in last_msg.tool_calls:\n", + " print(f\"🛠️ AGENT CALLING TOOL: {tool['name']}\")\n", + " print(f\"📝 ARGS: {json.dumps(tool['args'], indent=2)}\")\n", + " elif hasattr(last_msg, \"content\") and last_msg.content:\n", + " # Show a snippet of the AI's internal reasoning\n", + " content_snippet = last_msg.content[:150].replace('\\n', ' ')\n", + " print(f\"🧠 AI THOUGHT: {content_snippet}...\")\n", + "\n", + " # 2. Check for Data Extraction (JD/Resume results)\n", + " if \"JobDescriptionExtract_data\" in node_update:\n", + " jd = node_update[\"JobDescriptionExtract_data\"]\n", + " print(f\"✅ Extracted JD: {getattr(jd, 'job_title', 'Unknown')}\")\n", + " \n", + " if \"resume_data\" in node_update:\n", + " res = node_update[\"resume_data\"]\n", + " print(f\"✅ Extracted Resume for: {getattr(res, 'candidate_name', 'Unknown')}\")\n", + "\n", + " # 3. Check for the final output keys\n", + " if \"skill_gap_analysis_data\" in node_update:\n", + " print(\"🎯 Skill Gap Analysis Completed.\")\n", + " \n", + " if \"learning_roadmap\" in node_update or \"final_roadmap\" in node_update:\n", + " print(\"🏁 Final Roadmap Constructed.\")\n", + "\n", + " print(\"\\n\" + \"=\"*50 + \"\\n\")\n", + "\n", + " # Access the final state after the stream ends\n", + " final_state = graph.get_state(config)\n", + " print(\"✨ Stream Finished. Final state captured.\")\n", + " return final_state.values\n", + "\n", + "# --- Example Usage ---\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a36ecb1", + "metadata": {}, + "outputs": [], + "source": [ + "config = {\"configurable\": {\"thread_id\": \"debug_123\"}}\n", + "final_result = run_graph_with_stream(graph, initial_input, config)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e502949", + "metadata": {}, + "outputs": [], + "source": [ + "final_result" + ] + }, + { + "cell_type": "code", + "execution_count": null, "id": "53ba21aa", "metadata": {}, - "outputs": [ - { - "ename": "SyntaxError", - "evalue": "invalid syntax (1106862335.py, line 1)", - "output_type": "error", - "traceback": [ - " \u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[46]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[31m \u001b[39m\u001b[31m///break\u001b[39m\n ^\n\u001b[31mSyntaxError\u001b[39m\u001b[31m:\u001b[39m invalid syntax\n" - ] - } - ], + "outputs": [], "source": [ "///break" ] }, { "cell_type": "code", - "execution_count": 217, + "execution_count": null, "id": "5afbce5b", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'candidate_name': 'Chirayu Jain',\n", - " 'resume_text': 'Chirayu Jain\\nSOFTWARE DEVELOPER\\n+91-9462128878 | chirayujain93@gmail.com | LinkedIn | GitHub | CodeChef | Leetcode\\nEDUCATION\\nIndian Institute of Information Technology, Kota (CGPA-8.1)\\nKota, Rajasthan\\nB.Tech in Electronics and Communication Engineering\\n2022 - Present\\nWORK EXPERIENCE\\nNAV India\\nJan 2026 - Present\\nTechnology Intern\\nOnsite – Jaipur, Rajasthan\\n• As part of the AI/ML team, built Flask API for the RTA investor verification team, implementing 2\\nconfidence-scoring pipelines using OCR extraction (PyMuPDF, Docling) to parse PDF/forms and\\nextract 3 key KYC investor attributes, reducing verification time from 20–30 minutes to 2–3 minutes.\\n• Developed a scalable document intelligence pipeline with parallel CPU processing using Docling OCR,\\nsemantic chunking, and LLM-based extraction to process 60+ page financial documents and identify\\ninvestor/investee details, reducing extraction time by 35%.\\n• Implementing Cron-based background workers to generate automated weekly email analytics reports\\ntracking API accuracy and confidence match-mismatch scores, improving scoring precision and reducing\\noverall document processing latency by 40%.\\nVestbox\\nApril 2025 - May 2025\\nSoftware Development intern\\nRemote\\n• Developed Groceazy app in Flutter (MVVM), integrating 10+ modules for grocery service , order management\\nand Applied clean code practices (SOLID, modular design) for scalability.\\n• Implemented secure user authentication and role-based access, ensuring smooth login and data protection.\\n• Built image-to-item price matcher using APIs across 5+ platforms, enabling best-price suggestions.\\nPROJECTS\\nHostel Bites – IIITK Canteen App | Flutter, Dart, Firebase, Cloud Firestore, Mapbox API\\n• Developed a Flutter app for hostel residents to order food, increasing canteen efficiency by 35%.\\n• Implemented role-based access for students, staff, and admin, increasing operational efficiency by 40%.\\n• Integrated Firebase Auth, Firestore, Storage, and Mapbox API for secure logins, and live order tracking.\\n• GitHub: Live link\\nJobShield - Fake Job Detector | Flutter, Dart, Python, Flask, SQLite\\n• Developed a Flask+Python fake job detection API with a custom NLP model, with 95% classification accuracy.\\n• Built a responsive web interface using Flutter (Web), integrated with the backend, enabling seamless real-time\\ndetection for 500+ job postings.\\n• Github: Live Link\\nTECHNICAL SKILLS\\nLanguages: C/C++ , Java , Python , JavaScript, Dart\\nFrontend: Flutter, HTML/CSS, ReactJS\\nBackend: Firebase, REST APIs, Flask, Django, NodeJS, ExpressJS\\nDatabase Management: MySQL, MongoDB, SQLite\\nTools and Frameworks: GitHub, Android Studio, Visual Studio Code, MATLAB, Cursor, Github Copilot\\nLibraries: Provider, Bloc, Scikit-learn, tensorflow\\nACHIEVEMENTS & CERTIFICATIONS\\n• Ranked 81 in Inter IIIT Coding Contest Optigo, competing against top-tier programmers - View credential.\\n• Obtained Certification in Graph Algorithms from AlgoUniversity - View credential.\\n• Achieved a 4-star CodeChef rating and earned the Knight(Max. 1888) badge on LeetCode.\\n• Developed an e-waste facility locator application for SIH’23, successfully advancing to the regionals.',\n", - " 'file_path': 'C:\\\\Users\\\\ATHARVA\\\\Downloads\\\\my codes\\\\web\\\\AdaptiveEngineService\\\\AI_Engine_Evaluation\\\\Testcases\\\\Testresume\\\\ChirayuResume.pdf',\n", - " 'job_description': 'Job Title: Backend Developer\\n\\nCompany name: CodeForge\\nWe are hiring a Backend Developer to build scalable APIs and backend systems.\\n\\nResponsibilities:\\n- Develop REST APIs using FastAPI\\n- Design and manage PostgreSQL databases\\n- Implement authentication and authorization systems\\n- Optimize performance and scalability\\n\\nRequirements:\\n- Strong knowledge of Python\\n- Experience with FastAPI or Django\\n- Good understanding of SQL and database design\\n- Familiarity with Docker\\n\\nConstraints:\\n- Location: Pune only\\n- Full-time role ',\n", - " 'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'functions.search_courses:0', 'function': {'arguments': '{\"category\":\"Backend\",\"level\":\"beginner\",\"query\":\"FastAPI\"}', 'name': 'search_courses'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 32, 'prompt_tokens': 1498, 'total_tokens': 1530, 'completion_time': 0.089532947, 'completion_tokens_details': None, 'prompt_time': 0.110340762, 'prompt_tokens_details': {'cached_tokens': 256}, 'queue_time': 0.286648863, 'total_time': 0.199873709}, 'model_name': 'moonshotai/kimi-k2-instruct-0905', 'system_fingerprint': 'fp_05df423bab', 'service_tier': 'on_demand', 'finish_reason': 'tool_calls', 'logprobs': None, 'model_provider': 'groq'}, id='lc_run--019d1702-045e-7d30-83b2-34fc1fc18e7f-0', tool_calls=[{'name': 'search_courses', 'args': {'category': 'Backend', 'level': 'beginner', 'query': 'FastAPI'}, 'id': 'functions.search_courses:0', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 1498, 'output_tokens': 32, 'total_tokens': 1530, 'input_token_details': {'cache_read': 256}}),\n", - " ToolMessage(content=\"ID: CS-FAST-101\\nTitle: None\\nDescription: Title: REST API Development with FastAPI. Description: A project-driven course for building production-quality REST APIs with Python's FastAPI framework. Students learn to define typed routes with path and query parameters, leverage Pydantic models for automatic request validation and serialization, implement dependency injection, and secure endpoints using JWT-based Bearer token authentication.. Outcomes: Create REST endpoints with path and query params, Validate request/response with Pydantic models, Implement JWT-based authentication\\nPrerequisites: ['CS-PY-101']\\nDuration: 6 hours\\n---\", name='search_courses', id='80ad3f66-6e70-46f7-865b-cac12fb9a7e6', tool_call_id='functions.search_courses:0'),\n", - " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'functions.search_courses:1', 'function': {'arguments': '{\"category\":\"Database\",\"level\":\"beginner\",\"query\":\"PostgreSQL\"}', 'name': 'search_courses'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 32, 'prompt_tokens': 1671, 'total_tokens': 1703, 'completion_time': 0.085152771, 'completion_tokens_details': None, 'prompt_time': 0.133275603, 'prompt_tokens_details': {'cached_tokens': 256}, 'queue_time': 0.278479604, 'total_time': 0.218428374}, 'model_name': 'moonshotai/kimi-k2-instruct-0905', 'system_fingerprint': 'fp_241bc7119c', 'service_tier': 'on_demand', 'finish_reason': 'tool_calls', 'logprobs': None, 'model_provider': 'groq'}, id='lc_run--019d1702-0cdc-7c92-82c3-5e36a4f457ee-0', tool_calls=[{'name': 'search_courses', 'args': {'category': 'Database', 'level': 'beginner', 'query': 'PostgreSQL'}, 'id': 'functions.search_courses:1', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 1671, 'output_tokens': 32, 'total_tokens': 1703, 'input_token_details': {'cache_read': 256}}),\n", - " ToolMessage(content='ID: CS-DB-101\\nTitle: None\\nDescription: Title: SQL Fundamentals for Backend Developers. Description: A hands-on introduction to relational database theory and practice using SQLite and PostgreSQL. Students learn to model data with schemas, enforce referential integrity through primary and foreign keys, and interact with data using full CRUD operations and multi-table JOINs.. Outcomes: Write basic SELECT queries with filters, Understand Primary and Foreign Keys, Perform data insertion, updates, and deletions\\nPrerequisites: []\\nDuration: 4 hours\\n---', name='search_courses', id='fa947959-1024-432f-9378-c900002a6c0b', tool_call_id='functions.search_courses:1'),\n", - " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'functions.search_courses:2', 'function': {'arguments': '{\"category\":\"DevOps\",\"level\":\"beginner\",\"query\":\"Docker\"}', 'name': 'search_courses'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 32, 'prompt_tokens': 1831, 'total_tokens': 1863, 'completion_time': 0.087070476, 'completion_tokens_details': None, 'prompt_time': 0.058581579, 'prompt_tokens_details': {'cached_tokens': 1536}, 'queue_time': 0.279298899, 'total_time': 0.145652055}, 'model_name': 'moonshotai/kimi-k2-instruct-0905', 'system_fingerprint': 'fp_241bc7119c', 'service_tier': 'on_demand', 'finish_reason': 'tool_calls', 'logprobs': None, 'model_provider': 'groq'}, id='lc_run--019d1702-103b-77e0-8a41-482914c5607c-0', tool_calls=[{'name': 'search_courses', 'args': {'category': 'DevOps', 'level': 'beginner', 'query': 'Docker'}, 'id': 'functions.search_courses:2', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 1831, 'output_tokens': 32, 'total_tokens': 1863, 'input_token_details': {'cache_read': 1536}}),\n", - " ToolMessage(content='ID: CS-DOCKER-101\\nTitle: None\\nDescription: Title: Docker & Containerization Fundamentals. Description: A practical guide to application containerization using Docker. Students learn to write optimized multi-stage Dockerfiles, manage images and container lifecycles, configure bridge and overlay networking, use named volumes for data persistence, and orchestrate multi-service applications with Docker Compose for local and staging environments.. Outcomes: Write efficient multi-stage Dockerfiles, Manage container lifecycle and networking, Orchestrate multi-service apps with Docker Compose\\nPrerequisites: []\\nDuration: 5 hours\\n---\\nID: CS-LINUX-101\\nTitle: None\\nDescription: Title: Linux & Shell Scripting for Developers. Description: Practical Linux proficiency for software developers working in Unix-like environments. Covers the filesystem hierarchy, file permissions and ownership models, user and group management, process monitoring with ps/top/htop, cron job scheduling, and writing production-ready bash scripts for automation and deployment tasks.. Outcomes: Navigate and manage the Linux file system, Write bash scripts for automation, Manage processes, cron jobs, and system services\\nPrerequisites: []\\nDuration: 5 hours\\n---', name='search_courses', id='13508ff1-2f2c-4d4d-9fc5-706538e6b0ea', tool_call_id='functions.search_courses:2'),\n", - " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'functions.get_course_by_id:3', 'function': {'arguments': '{\"course_id\":\"CS-PY-101\"}', 'name': 'get_course_by_id'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 24, 'prompt_tokens': 2114, 'total_tokens': 2138, 'completion_time': 0.064076262, 'completion_tokens_details': None, 'prompt_time': 0.10578708, 'prompt_tokens_details': {'cached_tokens': 1280}, 'queue_time': 0.287427976, 'total_time': 0.169863342}, 'model_name': 'moonshotai/kimi-k2-instruct-0905', 'system_fingerprint': 'fp_05df423bab', 'service_tier': 'on_demand', 'finish_reason': 'tool_calls', 'logprobs': None, 'model_provider': 'groq'}, id='lc_run--019d1702-1326-7c90-a047-f6c2c1dfc78e-0', tool_calls=[{'name': 'get_course_by_id', 'args': {'course_id': 'CS-PY-101'}, 'id': 'functions.get_course_by_id:3', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 2114, 'output_tokens': 24, 'total_tokens': 2138, 'input_token_details': {'cache_read': 1280}}),\n", - " ToolMessage(content='{\\n \"course_id\": \"CS-PY-101\",\\n \"title\": \"Python Programming Fundamentals\",\\n \"level\": \"beginner\",\\n \"category\": \"Programming\",\\n \"description\": \"A comprehensive introduction to Python covering primitive and complex data types, control flow structures, function design, object-oriented programming principles, exception handling, and file I/O. Students build a strong syntactic and conceptual foundation before moving to applied domains.\",\\n \"learning_outcomes\": [\\n \"Write clean Python functions and classes\",\\n \"Understand list, dict, and set operations\",\\n \"Handle exceptions and file I/O\"\\n ],\\n \"prerequisites\": [],\\n \"estimated_duration_hours\": 6,\\n \"tags\": [\\n \"Python\",\\n \"Programming\",\\n \"OOP\"\\n ]\\n}', name='get_course_by_id', id='cb722ab5-a13d-44f8-8e3c-6c083a954525', tool_call_id='functions.get_course_by_id:3'),\n", - " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'functions.submit_final_roadmap:4', 'function': {'arguments': '{\"candidate_name\":\"Chirayu Jain\",\"onboarding_summary\":\"Chirayu possesses solid Python, Django, and general SQL experience, positioning him well for core backend responsibilities. However, he lacks exposure to FastAPI, PostgreSQL, and Docker, which are critical for the target role at CodeForge.\",\"roadmap\":[{\"course_id\":\"CS-PY-101\",\"is_foundation\":true,\"reasoning\":\"Prerequisite for FastAPI course - ensures strong Python foundation before API development\",\"sequence_order\":1,\"title\":\"Python Programming Fundamentals\"},{\"course_id\":\"CS-FAST-101\",\"is_foundation\":false,\"reasoning\":\"High priority gap - JD requires FastAPI for building modern APIs; candidate has Python but no FastAPI experience\",\"sequence_order\":2,\"title\":\"REST API Development with FastAPI\"},{\"course_id\":\"CS-DB-101\",\"is_foundation\":false,\"reasoning\":\"High priority gap - JD requires PostgreSQL; candidate has MySQL/MongoDB but no PostgreSQL exposure\",\"sequence_order\":3,\"title\":\"SQL Fundamentals for Backend Developers\"},{\"course_id\":\"CS-DOCKER-101\",\"is_foundation\":false,\"reasoning\":\"High priority gap - Docker required for containerization and deployment; candidate has no container tooling experience\",\"sequence_order\":4,\"title\":\"Docker \\\\u0026 Containerization Fundamentals\"}],\"target_role\":\"Backend Developer\"}', 'name': 'submit_final_roadmap'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 333, 'prompt_tokens': 2320, 'total_tokens': 2653, 'completion_time': 0.818676271, 'completion_tokens_details': None, 'prompt_time': 0.030364452, 'prompt_tokens_details': {'cached_tokens': 2048}, 'queue_time': 0.288163795, 'total_time': 0.849040723}, 'model_name': 'moonshotai/kimi-k2-instruct-0905', 'system_fingerprint': 'fp_05df423bab', 'service_tier': 'on_demand', 'finish_reason': 'tool_calls', 'logprobs': None, 'model_provider': 'groq'}, id='lc_run--019d1702-1520-7f92-88c9-57bc8a37404c-0', tool_calls=[{'name': 'submit_final_roadmap', 'args': {'candidate_name': 'Chirayu Jain', 'onboarding_summary': 'Chirayu possesses solid Python, Django, and general SQL experience, positioning him well for core backend responsibilities. However, he lacks exposure to FastAPI, PostgreSQL, and Docker, which are critical for the target role at CodeForge.', 'roadmap': [{'course_id': 'CS-PY-101', 'is_foundation': True, 'reasoning': 'Prerequisite for FastAPI course - ensures strong Python foundation before API development', 'sequence_order': 1, 'title': 'Python Programming Fundamentals'}, {'course_id': 'CS-FAST-101', 'is_foundation': False, 'reasoning': 'High priority gap - JD requires FastAPI for building modern APIs; candidate has Python but no FastAPI experience', 'sequence_order': 2, 'title': 'REST API Development with FastAPI'}, {'course_id': 'CS-DB-101', 'is_foundation': False, 'reasoning': 'High priority gap - JD requires PostgreSQL; candidate has MySQL/MongoDB but no PostgreSQL exposure', 'sequence_order': 3, 'title': 'SQL Fundamentals for Backend Developers'}, {'course_id': 'CS-DOCKER-101', 'is_foundation': False, 'reasoning': 'High priority gap - Docker required for containerization and deployment; candidate has no container tooling experience', 'sequence_order': 4, 'title': 'Docker & Containerization Fundamentals'}], 'target_role': 'Backend Developer'}, 'id': 'functions.submit_final_roadmap:4', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 2320, 'output_tokens': 333, 'total_tokens': 2653, 'input_token_details': {'cache_read': 2048}}),\n", - " ToolMessage(content='{\"candidate_name\": \"Chirayu Jain\", \"target_role\": \"Backend Developer\", \"onboarding_summary\": \"Chirayu possesses solid Python, Django, and general SQL experience, positioning him well for core backend responsibilities. However, he lacks exposure to FastAPI, PostgreSQL, and Docker, which are critical for the target role at CodeForge.\", \"roadmap\": [{\"course_id\": \"CS-PY-101\", \"title\": \"Python Programming Fundamentals\", \"reasoning\": \"Prerequisite for FastAPI course - ensures strong Python foundation before API development\", \"is_foundation\": true, \"sequence_order\": 1}, {\"course_id\": \"CS-FAST-101\", \"title\": \"REST API Development with FastAPI\", \"reasoning\": \"High priority gap - JD requires FastAPI for building modern APIs; candidate has Python but no FastAPI experience\", \"is_foundation\": false, \"sequence_order\": 2}, {\"course_id\": \"CS-DB-101\", \"title\": \"SQL Fundamentals for Backend Developers\", \"reasoning\": \"High priority gap - JD requires PostgreSQL; candidate has MySQL/MongoDB but no PostgreSQL exposure\", \"is_foundation\": false, \"sequence_order\": 3}, {\"course_id\": \"CS-DOCKER-101\", \"title\": \"Docker & Containerization Fundamentals\", \"reasoning\": \"High priority gap - Docker required for containerization and deployment; candidate has no container tooling experience\", \"is_foundation\": false, \"sequence_order\": 4}]}', name='submit_final_roadmap', id='99df306f-fe84-4993-8f25-11222813f82f', tool_call_id='functions.submit_final_roadmap:4'),\n", - " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'functions.submit_mermaid_visualization:5', 'function': {'arguments': '{\"mermaid_code\":\"flowchart TD\\\\n A([Start — Chirayu\\'s current skills]):::start\\\\n subgraph W1[\\\\\"Week 1 — Core foundation\\\\\"]\\\\n B[CS-PY-101\\\\nPython Programming Fundamentals]:::known\\\\n end\\\\n subgraph W2[\\\\\"Week 2 — API development\\\\\"]\\\\n C[CS-FAST-101\\\\nREST API Development with FastAPI]:::gap\\\\n end\\\\n subgraph W3[\\\\\"Week 3 — Database skills\\\\\"]\\\\n D[CS-DB-101\\\\nSQL Fundamentals for Backend Developers]:::gap\\\\n end\\\\n subgraph W4[\\\\\"Week 4 — Deployment\\\\\"]\\\\n E[CS-DOCKER-101\\\\nDocker \\\\u0026 Containerization Fundamentals]:::gap\\\\n end\\\\n Z([Role-ready — Backend Developer]):::done\\\\n A --\\\\u003e B\\\\n B --\\\\u003e C\\\\n C --\\\\u003e D\\\\n D --\\\\u003e E\\\\n E --\\\\u003e Z\\\\n classDef gap fill:#EEEDFE,stroke:#534AB7,color:#26215C\\\\n classDef known fill:#E1F5EE,stroke:#0F6E56,color:#085041\\\\n classDef start fill:#1D9E75,stroke:#0F6E56,color:#E1F5EE\\\\n classDef done fill:#534AB7,stroke:#3C3489,color:#EEEDFE\"}', 'name': 'submit_mermaid_visualization'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 314, 'prompt_tokens': 2999, 'total_tokens': 3313, 'completion_time': 0.62990454, 'completion_tokens_details': None, 'prompt_time': 0.144336228, 'prompt_tokens_details': {'cached_tokens': 2304}, 'queue_time': 0.287750454, 'total_time': 0.774240768}, 'model_name': 'moonshotai/kimi-k2-instruct-0905', 'system_fingerprint': 'fp_05df423bab', 'service_tier': 'on_demand', 'finish_reason': 'tool_calls', 'logprobs': None, 'model_provider': 'groq'}, id='lc_run--019d1702-19be-75f0-9d28-2375655030d0-0', tool_calls=[{'name': 'submit_mermaid_visualization', 'args': {'mermaid_code': 'flowchart TD\\n A([Start — Chirayu\\'s current skills]):::start\\n subgraph W1[\"Week 1 — Core foundation\"]\\n B[CS-PY-101\\nPython Programming Fundamentals]:::known\\n end\\n subgraph W2[\"Week 2 — API development\"]\\n C[CS-FAST-101\\nREST API Development with FastAPI]:::gap\\n end\\n subgraph W3[\"Week 3 — Database skills\"]\\n D[CS-DB-101\\nSQL Fundamentals for Backend Developers]:::gap\\n end\\n subgraph W4[\"Week 4 — Deployment\"]\\n E[CS-DOCKER-101\\nDocker & Containerization Fundamentals]:::gap\\n end\\n Z([Role-ready — Backend Developer]):::done\\n A --> B\\n B --> C\\n C --> D\\n D --> E\\n E --> Z\\n classDef gap fill:#EEEDFE,stroke:#534AB7,color:#26215C\\n classDef known fill:#E1F5EE,stroke:#0F6E56,color:#085041\\n classDef start fill:#1D9E75,stroke:#0F6E56,color:#E1F5EE\\n classDef done fill:#534AB7,stroke:#3C3489,color:#EEEDFE'}, 'id': 'functions.submit_mermaid_visualization:5', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 2999, 'output_tokens': 314, 'total_tokens': 3313, 'input_token_details': {'cache_read': 2304}}),\n", - " ToolMessage(content='flowchart TD\\n A([Start — Chirayu\\'s current skills]):::start\\n subgraph W1[\"Week 1 — Core foundation\"]\\n B[CS-PY-101\\nPython Programming Fundamentals]:::known\\n end\\n subgraph W2[\"Week 2 — API development\"]\\n C[CS-FAST-101\\nREST API Development with FastAPI]:::gap\\n end\\n subgraph W3[\"Week 3 — Database skills\"]\\n D[CS-DB-101\\nSQL Fundamentals for Backend Developers]:::gap\\n end\\n subgraph W4[\"Week 4 — Deployment\"]\\n E[CS-DOCKER-101\\nDocker & Containerization Fundamentals]:::gap\\n end\\n Z([Role-ready — Backend Developer]):::done\\n A --> B\\n B --> C\\n C --> D\\n D --> E\\n E --> Z\\n classDef gap fill:#EEEDFE,stroke:#534AB7,color:#26215C\\n classDef known fill:#E1F5EE,stroke:#0F6E56,color:#085041\\n classDef start fill:#1D9E75,stroke:#0F6E56,color:#E1F5EE\\n classDef done fill:#534AB7,stroke:#3C3489,color:#EEEDFE', name='submit_mermaid_visualization', id='268dc7c4-351a-421a-a07b-44de044949f9', tool_call_id='functions.submit_mermaid_visualization:5'),\n", - " AIMessage(content='', additional_kwargs={}, response_metadata={'token_usage': {'completion_tokens': 1, 'prompt_tokens': 3617, 'total_tokens': 3618, 'completion_time': 0.005691315, 'completion_tokens_details': None, 'prompt_time': 0.365105412, 'prompt_tokens_details': {'cached_tokens': 1792}, 'queue_time': None, 'total_time': 0.370796727}, 'model_name': 'moonshotai/kimi-k2-instruct-0905', 'system_fingerprint': 'fp_241bc7119c', 'service_tier': 'on_demand', 'finish_reason': 'stop', 'logprobs': None, 'model_provider': 'groq'}, id='lc_run--019d1702-1e26-7403-a6c0-1e500f580a24-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 3617, 'output_tokens': 1, 'total_tokens': 3618, 'input_token_details': {'cache_read': 1792}})],\n", - " 'skill_gap_analysis_data': SkillGapAnalysis(job_title='Backend Developer', candidate_name='Chirayu Jain', analyzed_gaps=[SkillGap(skill_name='FastAPI', gap_type='missing_foundation', priority='high', reasoning=\"JD requires FastAPI for building modern APIs; candidate's resume shows Python experience but no mention of FastAPI usage in skills, experience, or projects.\", target_competency='Develop high-performance asynchronous RESTful services using FastAPI'), SkillGap(skill_name='PostgreSQL', gap_type='missing_foundation', priority='high', reasoning='JD lists PostgreSQL as a required database; candidate has MySQL, SQLite, and MongoDB experience but no PostgreSQL exposure.', target_competency='Design, implement, and query relational databases using PostgreSQL'), SkillGap(skill_name='Docker', gap_type='missing_foundation', priority='high', reasoning=\"Docker is required for containerization and deployment; candidate's profile does not include Docker or any container tooling.\", target_competency='Containerize applications and orchestrate deployments with Docker')], is_fresher_adaptation_needed=False, executive_summary='Chirayu possesses solid Python, Django, and general SQL experience, positioning him well for core backend responsibilities. However, he lacks exposure to FastAPI, PostgreSQL, and Docker, which are critical for the target role at CodeForge.'),\n", - " 'resume_data': ResumeExtract(candidate_name='Chirayu Jain', job_title='Software Developer', skills=[Skill(name='C/C++', category='Backend'), Skill(name='Java', category='Backend'), Skill(name='Python', category='Backend'), Skill(name='JavaScript', category='Frontend'), Skill(name='Dart', category='Frontend'), Skill(name='Flutter', category='Frontend'), Skill(name='HTML/CSS', category='Frontend'), Skill(name='ReactJS', category='Frontend'), Skill(name='Firebase', category='Backend'), Skill(name='REST APIs', category='Backend'), Skill(name='Flask', category='Backend'), Skill(name='Django', category='Backend'), Skill(name='NodeJS', category='Backend'), Skill(name='ExpressJS', category='Backend'), Skill(name='MySQL', category='Backend'), Skill(name='MongoDB', category='Backend'), Skill(name='SQLite', category='Backend'), Skill(name='GitHub', category='Other'), Skill(name='Android Studio', category='Other'), Skill(name='Visual Studio Code', category='Other'), Skill(name='MATLAB', category='Other'), Skill(name='Cursor', category='Other'), Skill(name='Github Copilot', category='Other'), Skill(name='Provider', category='Other'), Skill(name='Bloc', category='Other'), Skill(name='Scikit-learn', category='ML'), Skill(name='tensorflow', category='ML')], experience=[ExperienceItem(job_title='Technology Intern', experience_type='internship', technologies=['Flask', 'PyMuPDF', 'Docling', 'OCR', 'LLM', 'Cron'], responsibilities=['Built Flask API for RTA investor verification with confidence-scoring pipelines', 'Developed scalable document intelligence pipeline with parallel CPU processing', 'Implementing Cron-based background workers for automated weekly email analytics reports']), ExperienceItem(job_title='Software Development Intern', experience_type='internship', technologies=['Flutter', 'MVVM', 'SOLID'], responsibilities=['Developed Groceazy grocery app with 10+ modules', 'Implemented secure user authentication and role-based access', 'Built image-to-item price matcher using APIs'])], projects=[ProjectItem(name='Hostel Bites – IIITK Canteen App', technologies=['Flutter', 'Dart', 'Firebase', 'Cloud Firestore', 'Mapbox API']), ProjectItem(name='JobShield - Fake Job Detector', technologies=['Flutter', 'Dart', 'Python', 'Flask', 'SQLite'])], certifications=[CertificationItem(name='Graph Algorithms Certification from AlgoUniversity', topics_covered=['Graph Algorithms'])], is_fresher=False),\n", - " 'extraction_error': None,\n", - " 'JobDescriptionExtract_data': JobDescriptionExtract(job_title='Backend Developer', company_name='CodeForge', location='Pune', employment_type='Full-time role', duration_months=None, is_fresher_allowed=None, skills_required=[SkillRequirement(name='Python', level=None), SkillRequirement(name='FastAPI', level=None), SkillRequirement(name='Django', level=None), SkillRequirement(name='SQL', level=None), SkillRequirement(name='PostgreSQL', level=None), SkillRequirement(name='Docker', level=None)], tools_technologies=None, responsibilities=[ResponsibilityItem(description='Develop REST APIs using FastAPI'), ResponsibilityItem(description='Design and manage PostgreSQL databases'), ResponsibilityItem(description='Implement authentication and authorization systems'), ResponsibilityItem(description='Optimize performance and scalability')], requirements=[RequirementItem(description='Strong knowledge of Python'), RequirementItem(description='Experience with FastAPI or Django'), RequirementItem(description='Good understanding of SQL and database design'), RequirementItem(description='Familiarity with Docker')], constraints=[ConstraintItem(type='location', value='Pune only'), ConstraintItem(type='employment type', value='Full-time role')]),\n", - " 'mermaid_code': 'flowchart TD\\n A([Start — Chirayu\\'s current skills]):::start\\n subgraph W1[\"Week 1 — Core foundation\"]\\n B[CS-PY-101\\nPython Programming Fundamentals]:::known\\n end\\n subgraph W2[\"Week 2 — API development\"]\\n C[CS-FAST-101\\nREST API Development with FastAPI]:::gap\\n end\\n subgraph W3[\"Week 3 — Database skills\"]\\n D[CS-DB-101\\nSQL Fundamentals for Backend Developers]:::gap\\n end\\n subgraph W4[\"Week 4 — Deployment\"]\\n E[CS-DOCKER-101\\nDocker & Containerization Fundamentals]:::gap\\n end\\n Z([Role-ready — Backend Developer]):::done\\n A --> B\\n B --> C\\n C --> D\\n D --> E\\n E --> Z\\n classDef gap fill:#EEEDFE,stroke:#534AB7,color:#26215C\\n classDef known fill:#E1F5EE,stroke:#0F6E56,color:#085041\\n classDef start fill:#1D9E75,stroke:#0F6E56,color:#E1F5EE\\n classDef done fill:#534AB7,stroke:#3C3489,color:#EEEDFE',\n", - " 'final_roadmap': {'candidate_name': 'Chirayu Jain',\n", - " 'onboarding_summary': 'Chirayu possesses solid Python, Django, and general SQL experience, positioning him well for core backend responsibilities. However, he lacks exposure to FastAPI, PostgreSQL, and Docker, which are critical for the target role at CodeForge.',\n", - " 'roadmap': [{'course_id': 'CS-PY-101',\n", - " 'is_foundation': True,\n", - " 'reasoning': 'Prerequisite for FastAPI course - ensures strong Python foundation before API development',\n", - " 'sequence_order': 1,\n", - " 'title': 'Python Programming Fundamentals'},\n", - " {'course_id': 'CS-FAST-101',\n", - " 'is_foundation': False,\n", - " 'reasoning': 'High priority gap - JD requires FastAPI for building modern APIs; candidate has Python but no FastAPI experience',\n", - " 'sequence_order': 2,\n", - " 'title': 'REST API Development with FastAPI'},\n", - " {'course_id': 'CS-DB-101',\n", - " 'is_foundation': False,\n", - " 'reasoning': 'High priority gap - JD requires PostgreSQL; candidate has MySQL/MongoDB but no PostgreSQL exposure',\n", - " 'sequence_order': 3,\n", - " 'title': 'SQL Fundamentals for Backend Developers'},\n", - " {'course_id': 'CS-DOCKER-101',\n", - " 'is_foundation': False,\n", - " 'reasoning': 'High priority gap - Docker required for containerization and deployment; candidate has no container tooling experience',\n", - " 'sequence_order': 4,\n", - " 'title': 'Docker & Containerization Fundamentals'}],\n", - " 'target_role': 'Backend Developer'}}" - ] - }, - "execution_count": 217, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "final_state" ] }, { "cell_type": "code", - "execution_count": 48, + "execution_count": null, "id": "25a6b5b4", "metadata": {}, "outputs": [], @@ -2006,18 +1953,10 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": null, "id": "26c10157", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✅ UI Payload successfully exported to ai_output.json\n" - ] - } - ], + "outputs": [], "source": [ "export_ui_payload(final_state)" ] @@ -2040,15 +1979,17 @@ { "cell_type": "code", "execution_count": null, - "id": "5b29b7ea", + "id": "83f3bd72", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "store_state=[]" + ] }, { "cell_type": "code", - "execution_count": 218, - "id": "ba358c8a", + "execution_count": null, + "id": "5b29b7ea", "metadata": {}, "outputs": [], "source": [ @@ -2058,147 +1999,155 @@ "from langgraph.checkpoint.memory import MemorySaver\n", "\n", "# --- Configuration & Paths ---\n", - "# Mapping resumes to the JSON files containing the Ground Truth data we created\n", + "\n", "test_map = [\n", - " {\"resume\": \"ATGPDF.pdf\", \"data\": \"atgdata.json\", \"name\": \"Atharva_Gaykar\"},\n", - " \n", + " {\"resume\": \"ATGPDF.pdf\", \"data\": \"atgdata.json\", \"name\": \"Atharva_Gaykar\"},\n", + " \n", "]\n", "\n", - "# Update these to your actual local paths where the files are stored\n", "RESUME_DIR = r\"C:\\Users\\ATHARVA\\Downloads\\my codes\\web\\AdaptiveEngineService\\AI_Engine_Evaluation\\Testcases\\Testresume\"\n", - "DATA_DIR = r\"C:\\Users\\ATHARVA\\Downloads\\my codes\\web\\AdaptiveEngineService\\AI_Engine_Evaluation\\Testcases\\data\"\n", - "OUTPUT_DIR = \"./predictions\" \n", + "DATA_DIR = r\"C:\\Users\\ATHARVA\\Downloads\\my codes\\web\\AdaptiveEngineService\\AI_Engine_Evaluation\\Testcases\\data\"\n", + "\n", + "# Windows-safe absolute output path\n", + "OUTPUT_DIR = os.path.join(os.getcwd(), \"predictions\")\n", "\n", "if not os.path.exists(OUTPUT_DIR):\n", " os.makedirs(OUTPUT_DIR)\n", "\n", + "\n", "# --- Helper Functions ---\n", "\n", - "def get_job_description_string(data_filename):\n", + "def get_job_description_string(data_filename: str) -> str | None:\n", " \"\"\"\n", - " Extracts the Job Description from the test data and formats it as a clean \n", - " string for the Extraction Node. Handles both 'job_description' and \n", - " 'job_description_requirements' keys.\n", + " Extracts the Job Description from the test data JSON and\n", + " formats it as a clean string for the extraction node.\n", " \"\"\"\n", " path = os.path.join(DATA_DIR, data_filename)\n", + "\n", + " if not os.path.exists(path):\n", + " print(f\"⚠️ Data file not found: {path}\")\n", + " return None\n", + "\n", " try:\n", - " with open(path, 'r', encoding='utf-8') as f:\n", + " with open(path, \"r\", encoding=\"utf-8\") as f:\n", " suite = json.load(f)\n", - " \n", - " # Extract from 'job_description' or 'job_description_requirements'\n", - " jd_obj = suite.get(\"job_description\") or suite.get(\"job_description_requirements\")\n", - " \n", - " if not jd_obj:\n", - " return None\n", - " \n", - " title = jd_obj.get(\"title\") or jd_obj.get(\"job_title\", \"N/A\")\n", - " desc = jd_obj.get(\"description\", \"\")\n", - " reqs = jd_obj.get(\"requirements\", [])\n", - " \n", - " # Format as a clean string for the LLM to analyze\n", - " jd_string = f\"JOB TITLE: {title}\\n\\n\"\n", - " if desc:\n", - " jd_string += f\"OVERVIEW: {desc}\\n\\n\"\n", - " jd_string += \"REQUIREMENTS:\\n\" + \"\\n\".join([f\"- {r}\" for r in reqs])\n", - " \n", - " return jd_string\n", + "\n", + " jd_obj = suite.get(\"job_description\") or suite.get(\"job_description_requirements\")\n", + "\n", + " if not jd_obj:\n", + " print(f\"⚠️ No JD key found in {data_filename}\")\n", + " return None\n", + "\n", + " title = jd_obj.get(\"title\") or jd_obj.get(\"job_title\", \"N/A\")\n", + " desc = jd_obj.get(\"description\", \"\")\n", + " reqs = jd_obj.get(\"requirements\", [])\n", + "\n", + " jd_string = f\"JOB TITLE: {title}\\n\\n\"\n", + " if desc:\n", + " jd_string += f\"OVERVIEW: {desc}\\n\\n\"\n", + " jd_string += \"REQUIREMENTS:\\n\" + \"\\n\".join([f\"- {r}\" for r in reqs])\n", + "\n", + " return jd_string\n", + "\n", " except Exception as e:\n", - " print(f\"Error loading JD from {data_filename}: {e}\")\n", + " print(f\"❌ Error loading JD from {data_filename}: {e}\")\n", " return None\n", "\n", - "def export_ui_payload(state):\n", + "\n", + "def export_ui_payload(state: dict) -> dict:\n", " \"\"\"\n", - " Extracts and formats state data for the UI payload.\n", - " Ensures Pydantic objects are converted to dicts using .model_dump().\n", + " Extracts required keys from graph state.\n", + " Converts Pydantic objects to dicts via .model_dump().\n", " \"\"\"\n", - " REQUIRED_KEYS = [\"candidate_name\", \"skill_gap_analysis_data\", \"mermaid_code\", \"final_roadmap\"]\n", + " REQUIRED_KEYS = [\n", + " \"candidate_name\",\n", + " \"skill_gap_analysis_data\",\n", + " \"mermaid_code\",\n", + " \"final_roadmap\",\n", + " ]\n", " ui_data = {}\n", "\n", " for key in REQUIRED_KEYS:\n", " val = state.get(key)\n", " if val is None:\n", " continue\n", - " \n", - " # If it's a Pydantic object, dump it to a dict\n", " if hasattr(val, \"model_dump\"):\n", " ui_data[key] = val.model_dump()\n", " else:\n", - " # If it's already a dict, list, or string (like mermaid_code)\n", " ui_data[key] = val\n", + "\n", " return ui_data\n", "\n", + "\n", "# --- Execution Loop ---\n", "\n", "def run_evaluation_suite(graph_instance):\n", " \"\"\"\n", - " Automates the graph execution for every resume in the test suite.\n", - " Saves the final UI-ready payloads as 'predicted_{name}.json'.\n", + " Runs the graph for every resume in test_map.\n", + " Saves UI-ready payloads as predicted_{name}.json in OUTPUT_DIR.\n", " \"\"\"\n", + " print(f\"\\n📁 Output directory: {OUTPUT_DIR}\\n\")\n", + "\n", " for case in test_map:\n", " print(f\"🚀 Processing: {case['resume']}...\")\n", - " \n", - " # 1. Prepare Inputs\n", - " jd_content = get_job_description_string(case['data'])\n", - " \n", + "\n", + " # 1. Validate resume file exists\n", + " resume_path = os.path.join(RESUME_DIR, case[\"resume\"])\n", + " if not os.path.exists(resume_path):\n", + " print(f\"⚠️ Resume not found, skipping: {resume_path}\")\n", + " continue\n", + "\n", + " # 2. Load JD string\n", + " jd_content = get_job_description_string(case[\"data\"])\n", " if not jd_content:\n", - " print(f\"⚠️ Skipping {case['resume']}: JD not found in {case['data']}\")\n", + " print(f\"⚠️ Skipping {case['resume']}: JD not found in {case['data']}\")\n", " continue\n", "\n", - " # The 'job_description' key must match your extraction node's expectation\n", + " # 3. Build initial state\n", " initial_input = {\n", - " \"candidate_name\": case['name'].replace(\"_\", \" \"),\n", - " \"file_path\": os.path.join(RESUME_DIR, case['resume']),\n", - " \"job_description\": jd_content, \n", - " \"resume_text\": None # Assuming input_node or extraction node loads the PDF\n", + " \"candidate_name\": case[\"name\"].replace(\"_\", \" \"),\n", + " \"file_path\": resume_path,\n", + " \"job_description\": jd_content,\n", + " \"resume_text\": None,\n", + " \"resume_data\": None,\n", + " \"extraction_error\": None,\n", + " \"JobDescriptionExtract_data\": None,\n", + " \"skill_gap_analysis_data\": None,\n", + " \"messages\": [],\n", + " \"mermaid_code\": None,\n", + " \"final_roadmap\": None,\n", " }\n", "\n", - " # 2. Invoke Graph with a unique thread\n", - " thread_id = str(uuid.uuid4())\n", - " config = {\"configurable\": {\"thread_id\": thread_id}}\n", - " \n", + " # 4. Invoke graph\n", + " config = {\"configurable\": {\"thread_id\": str(uuid.uuid4())}}\n", + "\n", " try:\n", - " # Execution\n", " final_state = graph_instance.invoke(initial_input, config=config)\n", - " \n", - " # 3. Process and Save UI Payload\n", - " prediction = export_ui_payload(final_state)\n", + "\n", + " store_state.append(final_state)\n", + "\n", + " print(f\"✅ Graph execution successful for {case['resume']}\\n\")\n", + "\n", + " # 5. Export payload\n", + " prediction = export_ui_payload(final_state)\n", " output_file = f\"predicted_{case['name'].lower()}.json\"\n", " output_path = os.path.join(OUTPUT_DIR, output_file)\n", - " \n", + "\n", " with open(output_path, \"w\", encoding=\"utf-8\") as f:\n", - " json.dump(prediction, f, indent=2)\n", - " \n", - " print(f\"✅ Success! Prediction saved to: {output_path}\")\n", - " \n", - " except Exception as e:\n", - " print(f\"❌ Error during graph execution for {case['resume']}: {e}\")\n", + " json.dump(prediction, f, indent=2, ensure_ascii=False)\n", "\n", - "# --- Example of Triggering ---\n" + " print(f\"✅ Saved: {output_path}\\n\")\n", + "\n", + " except Exception as e:\n", + " print(f\"❌ Error during graph execution for {case['resume']}: {e}\\n\")" ] }, { "cell_type": "code", - "execution_count": 219, + "execution_count": null, "id": "c1638a3f", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "🚀 Processing: ATGPDF.pdf...\n", - "📂 File path received: C:\\Users\\ATHARVA\\Downloads\\my codes\\web\\AdaptiveEngineService\\AI_Engine_Evaluation\\Testcases\\Testresume\\ATGPDF.pdf\n", - "📂 File exists: True\n", - "📄 Pages loaded: 1\n", - "📄 Text length: 2671\n", - "DEBUGGER: Sending 995 characters to JD Agent...\n", - "📄 Resume text length: 2671 chars\n", - "DEBUGGER SUCCESS: Extracted Senior ML & MLOps Architect\n", - "✅ Resume extracted: AI Engineer\n", - "✅ Success! Prediction saved to: ./predictions\\predicted_atharva_gaykar.json\n" - ] - } - ], + "outputs": [], "source": [ "if __name__ == \"__main__\":\n", " # Assuming your graph is already compiled and named 'graph'\n", @@ -2448,9 +2397,7 @@ "# --- Mapping & Paths ---\n", "mapping = [\n", " {\"data\": \"atgdata.json\", \"name\": \"Atharva_Gaykar\"},\n", - " {\"data\": \"buisnessdata.json\", \"name\": \"Business_Manager\"},\n", - " {\"data\": \"chefdata.json\", \"name\": \"Executive_Chef\"},\n", - " {\"data\": \"casemanager.json\", \"name\": \"Case_Manager\"}\n", + " \n", "]\n", "\n", "DATA_DIR = r\"C:\\Users\\ATHARVA\\Downloads\\my codes\\web\\AdaptiveEngineService\\AI_Engine_Evaluation\\Testcases\\data\"\n", @@ -2756,7 +2703,7 @@ ], "metadata": { "kernelspec": { - "display_name": "pro_env", + "display_name": "venv", "language": "python", "name": "python3" },