Skip to content

yamini-nlp/PrepSphere

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

109 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🎯 PrepSphere

Full-stack AI placement preparation platform β€” role-aligned learning roadmaps, AI interview prep, and outreach guidance β€” built as a controlled comparison of two LLM-integration reliability strategies: a direct synchronous proxy versus a schema-validated async job queue.

Live Demo: https://prepsphere000146.vercel.app

Repository: https://github.com/yamini-nlp/PrepSphere

Node.js LLM Queue Database Validation Frontend Backend License


πŸ’‘ Motivation

LLM APIs fail in ways that are easy to miss in development and expensive to discover in production: malformed output that passes JSON parsing, silent fallbacks on rate limits, and schema drift between model versions. Most application code collapses all of these into a single undifferentiated error. This project was built specifically to study that problem β€” implementing two integration strategies for the same generation tasks, measuring schema conformance across 120 evaluation runs, and identifying a latent observability gap where a real failure was entirely invisible to the caller. The platform β€” placement preparation β€” is the deployment context; the reliability comparison is the research question.


🧭 Overview

PrepSphere is a placement-preparation platform covering three stages of job-seeking β€” learning, interview practice, and application outreach β€” built as a Vanilla JS frontend on Vercel with a separate Node.js/Express backend on Render.

The project implements two parallel integration strategies for the same underlying problem (an LLM API call that can fail, return malformed output, or run long), rather than a single pipeline:

  1. A direct, synchronous proxy to the Groq API (Frontend/api/groq.js), which is what every live AI feature currently calls.
  2. An asynchronous job-queue backend (Express + BullMQ + MongoDB on Render), built with retry logic and output validation, but currently wired into the live frontend for authentication only β€” the generation queues are implemented and independently verified working, not yet the path users hit.

This split is documented rather than hidden: the Architecture and Limitations sections describe exactly which path serves which feature today.


🎯 Problem Statement

Placement preparation is typically fragmented across separate tools β€” a learning-roadmap site, a resume editor, an interview question bank, an outreach template generator β€” each requiring its own context and login. PrepSphere consolidates the learning, practice, and outreach stages into one application, using LLM generation tailored to a specific target role or job description rather than static, generic content.


🧩 What It Does

PrepSphere covers three stages of job-seeking in one application:

Learning & Orientation The Roadmap module generates a structured learning path β€” ordered steps, topics, curated resources, and project ideas β€” for any target role. Users choose between tech, non-tech, or conventional tracks before generation. Resume and cover letter generation follow the same pattern: role or JD in, structured content out.

Interview Practice MockIt is a hub that routes users to round-specific preparation pages for Group Discussion, Aptitude, Technical, HR, and JAM rounds β€” each with curated content, strategies, and examples. Two AI-powered modules sit alongside it: MockMyInterview (/mmi/) takes a job description and generates 5–7 core preparation topics plus 10 interview questions with expert answers; the AI MCQ Generator (/quiz/) takes any text and produces a 10-question multiple-choice quiz with indexed answers. Both call llama-3.3-70b-versatile via the Vercel serverless proxy. PrepMaster (/prepmaster/) provides targeted preparation notes for a curated set of companies β€” this is static reference content, not a dynamically generated dataset.

Outreach Guidance HireHub (/hirehub/) covers the application execution stage. It explains cold emailing, LinkedIn direct messaging, and post-rejection follow-up strategies, with sample templates and pro tips for each. This is curated static guidance, not AI generation.


πŸ—οΈ Architecture: Two Reliability Strategies

The core design choice is implementing both strategies β€” not picking one β€” so their behaviour under the same failure conditions can be directly compared.

Path A β€” Direct Proxy (live path for all AI generation today)

Browser β†’ fetch('/api/groq') β†’ Vercel serverless function β†’ Groq API β†’ JSON returned synchronously

No persistence. No retry. No schema validation. If the model returns syntactically valid JSON with a mismatched shape, it reaches the frontend unchanged.

Path B β€” Async Job Queue (implemented; auth is the only live route today)

Browser β†’ POST /api/{feature} β†’ Express (Render) β†’ BullMQ queue β†’ aiWorker.js β†’ Zod validation β†’ MongoDB Job document
                                                                         ↑
                                                               retry/backoff on validation failure

A POST returns 202 Accepted with a jobId. workers/aiWorker.js consumes all four queues, validates output against a Zod schema before marking a job complete, and triggers BullMQ's retry policy on failure rather than persisting a malformed result. GET /api/jobs/:jobId exposes job status for polling. The queue infrastructure has been independently exercised and works correctly. Only /api/auth/* is live from the frontend today.

Why both exist: Path A is simpler and lower-latency; Path B adds retry, persistence, and runtime schema enforcement. The comparison is the point. Migrating live generation from Path A to Path B is the first item in Future Work.


🧠 LLM Output Validation

The four generation functions in backend/aiService.js β€” generateRoadmap, generateQuiz, generateMockInterview, extractBuzzwords β€” call Groq with response_format: { type: "json_object" } where applicable. This guarantees syntactically valid JSON but not field-level correctness.

Path B validates every response against a Zod schema before persistence. Four schemas are defined in backend/schemas.js:

const QuizSchema = z.array(
  z.object({
    question: z.string(),
    options: z.array(z.string()).length(4),
    answer: z.number().int().min(0).max(3),
  })
);

A failed validation throws, which triggers BullMQ's retry policy rather than persisting a malformed result. Path A has no schema validation β€” the parsed response is returned to the page as-is.


πŸ“Š Evaluation & Findings

backend/eval/runEval.js runs each of the four generation functions against 5 fixed sample inputs, 6 times each β€” 30 runs per feature, 120 total β€” and validates every result against its Zod schema. Results are logged to backend/eval/results.json.

Feature Runs Passed Pass Rate
πŸ—ΊοΈ Roadmap 30 30 100.0%
🏷️ Buzzwords 30 30 100.0%
🎀 Interview 30 30 100.0%
πŸ“ Quiz 30 29 96.7%

πŸ” Finding: The single quiz failure raised ZodError: expected array, received null β€” meaning generateQuiz() returned its generic fallback (null) rather than propagating the underlying error. This exposed a structural problem: all four functions in aiService.js wrap their Groq calls in a try/catch that logs to console and returns a typed fallback rather than re-throwing. As a result, a transient API error, a rate limit, and a schema mismatch are indistinguishable to the caller β€” they all surface as the same fallback. This was not assumed; it was discovered through the evaluation harness. It is documented as a known observability gap and is the first item in Future Work.

Scope: schema conformance only. Semantic quality, relevance, and factual correctness of generated content are not measured.


βš™οΈ Key Design Decisions

Component Choice Rationale
πŸ€– LLM llama-3.3-70b-versatile via Groq Low-latency inference compatible with synchronous Path A requests
πŸ“¬ Queue BullMQ + Upstash Redis Retry/backoff without managing dedicated queue infrastructure
πŸ—„οΈ Database MongoDB Atlas + Mongoose Schema flexibility across heterogeneous generation outputs; backs Path B Job collection with 1-hour TTL
βœ… Validation Zod (Path B only) Runtime enforcement of expected output shape before persistence
πŸ” Auth JWT, stateless No server-side session store required
🌐 Frontend Vanilla JS, no build step Zero pipeline; direct static deployment to Vercel

πŸ› οΈ Tech Stack

Layer Technology
Frontend HTML5, CSS3, Vanilla JavaScript
AI proxy (Path A) Vercel serverless function (api/groq.js)
Backend (Path B) Node.js, Express β€” deployed on Render
LLM Groq API, llama-3.3-70b-versatile
Schema validation Zod
Queue BullMQ, Upstash Redis
Database MongoDB Atlas, Mongoose
Auth JWT (jsonwebtoken)

πŸ“ Repository Structure

PrepSphere/
β”œβ”€β”€ backend/
β”‚   β”œβ”€β”€ server.js               # Express app: route mounting, CORS, error handling
β”‚   β”œβ”€β”€ aiService.js            # 4 generation functions: roadmap, quiz, interview, buzzwords
β”‚   β”œβ”€β”€ schemas.js              # Zod schemas: RoadmapSchema, QuizSchema, InterviewSchema, BuzzwordsSchema
β”‚   β”œβ”€β”€ workers/
β”‚   β”‚   └── aiWorker.js         # BullMQ worker β€” consumes all four queues
β”‚   β”œβ”€β”€ queues/
β”‚   β”‚   β”œβ”€β”€ index.js            # Queue definitions
β”‚   β”‚   └── redisConnection.js
β”‚   β”œβ”€β”€ routes/
β”‚   β”‚   β”œβ”€β”€ auth.js             # POST /api/auth/register, /login
β”‚   β”‚   └── jobs.js             # GET /api/jobs/:jobId
β”‚   β”œβ”€β”€ middleware/
β”‚   β”‚   β”œβ”€β”€ authMiddleware.js
β”‚   β”‚   └── rateLimiter.js      # Redis-backed, per-route limits
β”‚   β”œβ”€β”€ models/
β”‚   β”‚   β”œβ”€β”€ User.js
β”‚   β”‚   └── Job.js              # TTL index, 1-hour expiry
β”‚   └── eval/
β”‚       β”œβ”€β”€ runEval.js          # 120-run schema-conformance harness
β”‚       └── results.json        # Latest run output
β”œβ”€β”€ Frontend/
β”‚   β”œβ”€β”€ api/
β”‚   β”‚   β”œβ”€β”€ groq.js             # Direct Groq proxy (Path A)
β”‚   β”‚   └── schemas.js          # Zod schemas (reference copy)
β”‚   β”œβ”€β”€ js/config.js            # API_BASE_URL (env-aware)
β”‚   β”œβ”€β”€ roadmap/                # tech.html, nontech.html, conventional.html, pathfinder.html
β”‚   β”œβ”€β”€ mmi/                    # AI: job-description β†’ topics + 10 Q&A
β”‚   β”œβ”€β”€ quiz/                   # AI: text β†’ 10-question MCQ
β”‚   β”œβ”€β”€ MockIt/                 # Hub β†’ round-specific prep pages
β”‚   β”œβ”€β”€ gd/ hr/ technical/ aptitude/ Jam/   # Static round prep content
β”‚   β”œβ”€β”€ hirehub/                # Static outreach guidance: coldmail, dm, afterrej
β”‚   β”œβ”€β”€ prepmaster/             # Static company prep notes
β”‚   β”œβ”€β”€ resume/ coverletter/    # AI generation pages
β”‚   β”œβ”€β”€ dashboard/
β”‚   β”œβ”€β”€ login.html register.html
β”‚   └── index.html
β”œβ”€β”€ LICENSE
└── README.md

πŸš€ Local Setup

Prerequisites: Node.js 18+, MongoDB Atlas URI, Groq API key, Upstash Redis instance.

git clone https://github.com/yamini-nlp/PrepSphere.git
cd PrepSphere/backend && npm install

backend/.env:

MONGO_URI=your_mongodb_connection_string
GROQ_API_KEY=your_groq_api_key
JWT_SECRET=your_jwt_secret
REDIS_URL=rediss://default:your_password@your-host.upstash.io:6379
ADMIN_PASSWORD=your_bull_board_password

⚠️ Use rediss:// (TLS), not redis:// β€” Upstash rejects plain connections.

# Terminal 1 β€” API server
node server.js

# Terminal 2 β€” Queue worker
node workers/aiWorker.js

API: http://localhost:5000 Β· Bull Board: http://localhost:5000/admin/queues

# Frontend
cd ../Frontend && python -m http.server 5000

# Run evaluation harness
cd backend && node eval/runEval.js
# Writes eval/results.json and prints pass-rate summary

πŸ”’ Security

  • .env excluded from version control via backend/.gitignore.
  • CORS restricted to an explicit allow-list (localhost, *.vercel.app, *.github.io) β€” no wildcard.
  • Groq API key never reaches the browser: Path A's key lives in Vercel's serverless environment; Path B's lives in Render's β€” neither is shipped in frontend JavaScript.

⚠️ Limitations

  • Path B not yet serving live generation. The retry-backed, schema-validated queue system is implemented and verified; every live AI feature still calls the unvalidated Path A proxy.
  • Error-swallowing in aiService.js. All four generation functions return a typed fallback on failure rather than re-throwing. Provider errors, rate limits, and schema mismatches are indistinguishable to the caller. Identified through the evaluation harness.
  • Schema conformance only. The evaluation harness does not measure semantic quality, relevance, or factual correctness of generated content.
  • HireHub and round prep pages are static. Cold email, DM, after-rejection templates and the GD/HR/Technical/Aptitude/JAM pages are curated static content β€” not AI-generated.
  • PrepMaster company list is static. Reference content for a curated set of companies; not dynamically maintained.
  • Plain-text input only. No PDF parsing or document upload for resume or job description fields.
  • No cross-session state. No mechanism to track or resume preparation progress across sessions.
  • English only.

πŸ”­ Future Work

  • Re-throw structured error objects from aiService.js so evaluation runs can distinguish provider errors, rate limits, and schema mismatches.
  • Migrate live AI generation from Path A to Path B, applying retry, rate limiting, and persistence to what users actually experience.
  • Extend evaluation beyond schema conformance to semantic quality assessment.
  • Add PDF resume parsing with structured extraction.
  • Add cross-session progress tracking.

Built by Yamini G

About

AI placement-prep platform built as a controlled comparison of two LLM reliability strategies: a live Groq proxy vs. a BullMQ async queue with Zod schema validation.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors