LexiFeed is a full-stack web application that helps users practice English interview and speaking skills using AI-driven question generation, voice transcription, smart resume processing, and session feedback. It also includes vocabulary building and personal goal-tracking tools.
- Secure auth with JWT-based login/signup.
- CEFR Initial English Level Assessment — instantly places new users in the right difficulty band (A1-C2) using fast static content for zero-latency onboarding.
- AI-powered interview question generation from company, role, resume context, and a planner-generated interview blueprint.
- Agentic interview analysis — context-aware answer evaluation that tracks patterns and competency development across a full session.
- Voice transcription and analysis for spoken answers (local, free transcription via faster-whisper), covering both content and delivery (filler words, pace, structure).
- Follow-up question generation based on candidate responses, including voice-aware follow-ups that reference what was actually said.
- Reading practice mode with AI-generated passages and TV-news-anchor scripts across difficulty levels, plus pronunciation analysis.
- Candidate Intelligence — resume parsing, job description analysis, and resume/JD match scoring.
- Presentation upload preview — converts uploaded
.ppt/.pptxfiles to slide images. - Vocabulary builder — save words/idioms/phrases encountered during practice, plus a daily "Word of the Day".
- Goal tracking — create practice goals with deadlines, track progress and streaks, and view dashboard stats.
- Session feedback and progress stats saved per user.
- Modern frontend built with Next.js, TypeScript, Tailwind CSS.
- Next.js (App Router)
- TypeScript
- Tailwind CSS
- React
- Lucide icons
- React hooks + custom auth state
- Flask 3.x
- Flask-CORS
- Flask-SQLAlchemy / SQLite
- JWT auth via custom Flask middleware (
PyJWT) - Google Gemini (
google-genai) — used directly by the AI/voice/agentic-analysis services for question generation, feedback, and analysis. - Configurable provider abstraction (
backend/llm/,AI_PROVIDER) — OpenRouter / OpenAI / Ollama / Gemini — used by thebackend/agents/interview-agent pipeline. - faster-whisper — local, free speech-to-text transcription.
- PyMuPDF, pdfplumber, PyPDF2, python-docx — resume/PDF/presentation parsing.
LexiFeed/
├── backend/
│ ├── app.py
│ ├── config.py
│ ├── requirements.txt
│ ├── agents/ # Agentic interview pipeline (provider-abstracted)
│ ├── llm/ # AI_PROVIDER abstraction (OpenRouter/OpenAI/Ollama/Gemini)
│ ├── models/
│ ├── routes/
│ ├── services/
│ └── utils/
└── frontend/
├── app/
├── components/
├── lib/
├── hooks/
└── package.json
- Python 3.8+
- Node.js 18+
- npm
- LibreOffice (only required for the PPT upload preview feature — see below)
-
Open a terminal and navigate to the backend folder:
cd backend -
Create and activate a virtual environment:
- Windows PowerShell:
python -m venv venv .\venv\Scripts\Activate.ps1 - macOS/Linux:
python -m venv venv source venv/bin/activate
- Windows PowerShell:
-
Install dependencies:
pip install -r requirements.txt
-
Create a
.envfile insidebackend/:SECRET_KEY=your_app_secret_key_here JWT_SECRET_KEY=your_jwt_secret_key_here DATABASE_URL=sqlite:///app.db # Used directly by services/ (ai_service, voice_service, agentic_analysis) GEMINI_API_KEY=your_gemini_api_key_here # Used by the backend/agents/ pipeline via the AI_PROVIDER abstraction AI_PROVIDER=openrouter AI_MODEL=openrouter/free OPENROUTER_API_KEY=your_openrouter_api_key_here OLLAMA_API_URL=http://127.0.0.1:11434
Get a free Gemini API key at https://aistudio.google.com/app/apikey Get a free OpenRouter API key at https://openrouter.ai/keys —
openrouter/freeis OpenRouter's own auto-router, which picks whichever free model currently has capacity (recommended over pinning a single popular free model likemeta-llama/llama-3.3-70b-instruct:free, which gets rate-limited across all OpenRouter users during busy periods). No credit card required. Browse individual free models (:freesuffix) at https://openrouter.ai/models?supported_parameters=free -
Start the backend server:
python app.py
-
Verify the backend is running:
http://localhost:5000/api/healthapp.dbis generated automatically insidebackend/instance/when the backend starts.
- Windows: Download and install from libreoffice.org. If installed at a non-default path, update
LIBREOFFICE_PATHinbackend/routes/presentation_upload.py. - macOS:
brew install --cask libreoffice - Ubuntu/Debian:
sudo apt update && sudo apt install libreoffice -y
Verify with:
# Windows (PowerShell)
& "C:\Program Files\LibreOffice\program\soffice.exe" --version
# macOS / Linux
soffice --versionIf PPT preview shows only 1 slide, confirm LibreOffice is fully installed and the path in
presentation_upload.pyis correct.
-
Open a separate terminal and go to the frontend folder:
cd frontend -
Install dependencies:
npm install
-
Create a
.env.localfile infrontend/:NEXT_PUBLIC_API_URL=http://localhost:5000/api GROQ_API_KEY=your_groq_api_key_here GEMINI_API_KEY=your_gemini_api_key_here PEXELS_API_KEY=your_pexels_api_key_here
Key Where to get it Cost GROQ_API_KEYhttps://console.groq.com Free GEMINI_API_KEYhttps://aistudio.google.com/app/apikey Free PEXELS_API_KEYhttps://www.pexels.com/api/ Free -
Run the frontend:
npm run dev
-
Open the app at:
http://localhost:3000
- Sign up:
POST /api/auth/signup - Login:
POST /api/auth/login - Profile:
GET /api/auth/me
The frontend stores the returned JWT token and sends it in the Authorization: Bearer <token> header for protected endpoints.
POST /api/auth/signupPOST /api/auth/loginGET /api/auth/me
GET /api/assessment/status— Whether the current user has completed the placement test.GET /api/assessment/start— Builds a 5-part placement test (grammar, vocabulary, reading, listening, speaking) mixing a static item bank with AI-generated passages.POST /api/assessment/submit— Scores the completed test and stores the resulting CEFR level (A1-C2) on the user's profile.
POST /api/interview/start— Starts a new interview session and returns AI-generated questions. Supports multipart form data, an optional resume upload, and an optionalnum_questionsfield (1-20, default 5) to control how many questions are generated.POST /api/interview/questions/generate— Generates a blueprint-backed interview question set from the latest plan and candidate profile.GET /api/interview/questions— Lists generated questions for the current candidate profile.GET /api/interview/questions/<question_id>— Fetches a single stored generated question.POST /api/interview/followup— Generates a follow-up question from the candidate answer.POST /api/interview/feedback— Saves interview transcript and returns AI feedback.GET /api/interview/sessions— Retrieves a user's saved session history.GET /api/interview/stats— Returns aggregated progress metrics and streak data.POST /api/interview/progress/save— Upserts an in-progress interview session (questions, answered Q&A pairs, current position) so a page refresh mid-interview doesn't lose progress.GET /api/interview/progress/active— Returns the user's most recent in-progress attempt, used to show a "Resume / Start Fresh" prompt.DELETE /api/interview/progress/<session_key>— Discards a saved in-progress attempt.POST /api/interview/generate/POST /api/interview/upload-resume— Legacy endpoints.
POST /api/candidate/resume/upload— Upload a PDF resume and extract structured candidate information.POST /api/candidate/jd/analyze— Analyze a pasted job description and extract required skills, technologies, responsibilities, preferred experience, and domain tags.GET /api/candidate/profile— Return the latest candidate profile including resume data, JD data, and match metadata.GET /api/candidate/match— Generate or refresh match results between the resume and JD.
POST /api/voice/transcribePOST /api/voice/analyzePOST /api/voice/followupPOST /api/voice/analyze-agenticPOST /api/voice/session-comparison
- Routes under
/api/practice/reading— seebackend/routes/reading.pyfor AI passage/news-script generation and pronunciation analysis. - Passage generation accepts
level(CEFR) andlength(short/medium/long);leveldefaults to the requesting user's assessed CEFR level when not passed explicitly. - Every generated passage is logged to
ReadingPassageHistory, and the last 15 titles (mode-matched, 30-day window) are excluded from future generations to avoid repeats.
GET /api/vocabulary— List the current user's saved words/idioms/phrases, newest first.POST /api/vocabulary— Save an entry (idempotent — saving the same entry twice returns the existing row).DELETE /api/vocabulary/<entry_id>— Remove a saved entry.
GET /api/goals— List current user's goals.POST /api/goals— Create a goal.GET /api/goals/<id>— Get a single goal.PUT /api/goals/<id>— Edit a goal (title/description/target/deadline).DELETE /api/goals/<id>— Delete a goal.POST /api/goals/<id>/progress— Update progress (increment or set).GET /api/goals/stats— Dashboard summary stats (totals, streaks, average progress).
POST /api/presentation/upload-preview— Upload.pptx/.ppt, returns slide images (requires LibreOffice + PyMuPDF).
GET /api/health
- The frontend uses
frontend/lib/api.tsfor most interview-related API calls, including blueprint-based question generation. - Candidate Intelligence adds backend services and routes for structured resume + JD parsing (
backend/services/resume_parser.py,backend/services/jd_parser.py,backend/services/match_service.py). - The
backend/agents/pipeline uses theAI_PROVIDERabstraction (backend/llm/), supportingOpenRouter(default),OpenAI,Ollama, andGemini. The newer AI/voice/agentic-analysis services (backend/services/ai_service.py,voice_service.py,agentic_analysis.py) call Google Gemini directly viaGEMINI_API_KEYinstead. - The backend CORS policy allows local development requests from
localhost/127.0.0.1and standard private-network IP ranges (seebackend/app.py), so the app can be reached from other devices on the same network without extra CORS config. - SQLAlchemy
metadatacolumn: model classes usemeta_datato represent JSON metadata fields, to avoid conflicting with SQLAlchemy's internalmetadataclass property. These map to the"metadata"column in the database.
- Flask, Flask-CORS, Flask-SQLAlchemy, SQLAlchemy, Werkzeug
- PyJWT, python-dotenv
- openai (OpenRouter/OpenAI-compatible client), requests, google-genai
- faster-whisper, ctranslate2
- pdfplumber, PyPDF2, python-docx, PyMuPDF, pillow
See backend/requirements.txt for the full pinned list. If you add a feature that needs a new library, add it there and re-run pip install -r requirements.txt.
- Next.js, React, TypeScript, Tailwind CSS
- react-hook-form, zod, lucide-react
- pptxgenjs, jszip
To access LexiFeed from another device on the same network (phone, tablet, another computer):
- Find your machine's local IP address (e.g.
10.221.6.136) viaipconfig(Windows) orifconfig/hostname -I(Linux/macOS). - Start the backend — it already binds to
0.0.0.0, so it's reachable athttp://<YOUR_IP>:5000. - Start the frontend bound to all interfaces:
npm run dev -- --host 0.0.0.0, reachable athttp://<YOUR_IP>:3000. - Update
frontend/.env.local:NEXT_PUBLIC_API_URL=http://<YOUR_IP>:5000/api. - CORS already allows private-network IP ranges — no extra changes needed.
- Open
http://<YOUR_IP>:3000from the other device's browser.
Note: In production, replace these dev-only CORS settings with a proper domain whitelist.
- Ensure the virtual environment is activated and
requirements.txtinstalled correctly. - Check
.envincludesGEMINI_API_KEYand, if using the agentic pipeline's default provider,OPENROUTER_API_KEY.
- Verify the backend is running on port
5000. - Confirm
NEXT_PUBLIC_API_URLishttp://localhost:5000/api. - If CORS errors appear, review the allowed origins in
backend/app.py.
- Make sure LibreOffice is fully installed and the path in
backend/routes/presentation_upload.pymatches your installation. - Confirm
PyMuPDFinstalled correctly (pip show pymupdf).
- Check that
GROQ_API_KEYis set infrontend/.env.localand restartnpm run devafter adding it.
cd backend
rm instance/app.db
python app.pybackend/services/resume_parser.pyparses uploaded PDF resumes usingpdfplumberorPyPDF2.- Extracted fields include personal info, education, skills, programming languages, frameworks, databases, cloud technologies, projects, certifications, tools, and soft skills.
- Parsed values are normalized and deduplicated so the backend stores canonical names such as
Python,AWS, andSaaS.
backend/services/jd_parser.pyanalyzes pasted JD text, extracting required skills, technologies, programming languages, frameworks, database technologies, tools, responsibilities, preferred experience, and preferred domain.
backend/services/match_service.pycompares resume and JD structured data, producing matching/missing skills, a skill match percentage, project/technology match, strengths, and improvement areas.- Match data is stored via
backend/models/candidate_profile.py.
- Resume profile JSON includes:
candidate_name,email,phone,education,skills,projects,internships,certifications,tools,soft_skills. - JD summary JSON includes:
required_skills,required_technologies,programming_languages,frameworks,database_technologies,required_tools,preferred_experience,responsibilities,preferred_domain. - Match summary JSON includes:
matching_skills,missing_skills,skill_match_percentage,project_match,technology_match,strength_areas,improvement_areas.
Unit and integration tests for the production-ready interview platform features live in backend/tests/ (test_production_features.py, test_candidate_intelligence.py, test_interview_planning.py). Run them from the backend/ directory with your virtual environment active:
cd backend
python -m pytest tests/backend/templates/ is reserved for deployment/documentation templates for production use and is currently empty.
- Assessment answer keys are cached in an in-process dict (
routes/assessment.py), keyed by user ID. This works for a single-process deployment; a multi-worker/multi-process production deploy should move this to the database or Redis so/api/assessment/startand/api/assessment/submitalways hit the same process. - Interview progress resume restores state to right after the last completed answer. An in-progress follow-up exchange mid-chain isn't separately persisted, so refreshing mid-follow-up drops that one follow-up round (the main answer and its analysis are still saved).
- Voice transcription (
transcribe_audioinvoice_service.py) uses localfaster-whisperand needs no API key, but text-based follow-up/analysis for voice answers still goes through the configuredAI_PROVIDER. - The
backend/agents/and orchestrator pipeline (blueprint planner, evaluation agent, recommendation agent) is reachable via/api/interview/plan/*and/api/interview/session/*, but the primary practice page (/practice/interview) uses the simpler, self-contained flow described above rather than this orchestrator — worth an audit before relying on the orchestrator end-to-end. - OpenRouter free-tier rate limits: if you see repeated fallback/template content instead of real AI responses, you may be hitting OpenRouter's free-tier cap (20 requests/min, 50/day without adding credit; 1,000/day once a one-time $10 balance is added). Setting
AI_MODEL=openrouter/freelets OpenRouter auto-route to whichever free model currently has capacity, instead of hammering a single congested model. See https://openrouter.ai/docs/api-reference/limits for current numbers.
Fork the repository, create a branch, implement your changes, and open a pull request with a clear description. Keep backend and frontend changes separate when possible.
You can view the project demonstration videos using the Google Drive link below:
🔗 https://drive.google.com/drive/folders/1wMZ62Scx87hwHFD8_HgB3Xq5Kzr6kPns?usp=sharing