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
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.
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:
- A direct, synchronous proxy to the Groq API (
Frontend/api/groq.js), which is what every live AI feature currently calls. - 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.
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.
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.
The core design choice is implementing both strategies β not picking one β so their behaviour under the same failure conditions can be directly compared.
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.
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.
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.
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.
| 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 |
| 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) |
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
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 installbackend/.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
β οΈ Userediss://(TLS), notredis://β Upstash rejects plain connections.
# Terminal 1 β API server
node server.js
# Terminal 2 β Queue worker
node workers/aiWorker.jsAPI: 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.envexcluded from version control viabackend/.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.
- 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.
- Re-throw structured error objects from
aiService.jsso 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