Skip to content

lloyd1515/Mini_AI_Interviewer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

INSIGHT - Mini AI Interviewer

AI-powered qualitative research interviews. 3-5 adaptive questions. One structured analysis.

INSIGHT conducts short, intelligent interviews on any topic using Google Gemini. It dynamically adjusts interview depth, generates contextual follow-up questions based on your actual responses, and delivers a structured executive summary with sentiment analysis, key themes, and keyword extraction.

Built as a full-stack application with a clean four-layer backend architecture and a reactive single-page frontend.


Core Features

  • Dynamic Interview Depth - The AI evaluates topic complexity and selects 3-5 questions accordingly (simple topics get 3, deeply technical ones get 5).
  • Sequential Contextual Questions - Each follow-up is generated from the full transcript so far, not from a pre-built list. The conversation adapts to what you actually say.
  • Structured Analysis Output - Executive summary, key themes, sentiment (positive/neutral/negative) with rationale, notable quotes, and keyword extraction.
  • Full Transcript Persistence - SQLite with WAL mode for concurrent access. Every session is stored and exportable as JSON.
  • LLM Resilience - Every Gemini call has a hardcoded fallback. JSON sanitization strips markdown fences and formatting leaks. The app never crashes from an LLM failure.
  • Session Deep-Linking - URL-based session hydration. Share or bookmark /?session_id=xyz to return directly to a completed analysis.
  • Knowledge Archive - Full interview history with optimistic-delete UI and relative timestamps.

Assignment Requirements Verification

# Requirement Status Implementation
1 Start interview on a chosen topic Done POST /api/start with AI-generated topic suggestions
2 Generate 3-5 sequential AI questions Done evaluate_interview_depth() + one-at-a-time generation
3 Collect answers interactively Done Stateful POST /api/answer loop with index tracking
4 AI-generated summary at end Done POST /api/finish returns structured JSON analysis
5 Store transcript and summary Done SQLite transcripts table + JSON file export
Bonus Sentiment scoring + keyword extraction Done Integrated in summary JSON schema

Technical Architecture

[ Browser ]                          [ Server ]
     |                                    |
     |  POST /api/start {topic}           |
     |----------------------------------->|
     |                                    |--- interview_manager.py
     |                                    |      |
     |                                    |      |--- llm_client.py
     |                                    |      |      |--- evaluate_interview_depth(topic)
     |                                    |      |      |--- generate_first_question(topic)
     |                                    |      |      |        |
     |                                    |      |      |     [ Gemini API ]
     |                                    |      |      |        |
     |                                    |      |--- database.py
     |                                    |             |--- create_session()
     |                                    |             |--- save_question()
     |  {session_id, question, total}     |
     |<-----------------------------------|
     |                                    |
     |  POST /api/answer {answer, index}  |   x3-5 times
     |----------------------------------->|
     |                                    |--- save_answer() + generate_next_question()
     |  {next_question, index, done}      |
     |<-----------------------------------|
     |                                    |
     |  POST /api/finish {session_id}     |
     |----------------------------------->|
     |                                    |--- generate_summary(topic, transcript)
     |                                    |--- save_summary() -> SQLite
     |  {summary: {themes, sentiment...}} |
     |<-----------------------------------|

Backend - Four Clean Layers

File Responsibility Depends On
app.py HTTP routing, validation, rate limiting interview_manager, database, llm_client
interview_manager.py Business orchestration, state machine database, llm_client
llm_client.py All Gemini API calls, JSON sanitization, fallbacks prompts
database.py SQLite persistence, WAL mode, typed queries -
prompts.py Pure prompt templates (zero logic) -

No LLM logic leaks into routes. No database calls in the LLM layer. No business logic in the API layer.

Frontend - React + TypeScript + Tailwind

Component Role
InterviewHub.tsx Main page: topic selection, Q&A flow, analysis view, summary display
History.tsx Knowledge archive with optimistic delete and session navigation
AppSidebar.tsx Collapsible sidebar with hover-expand animation
AppLayout.tsx Shell layout with responsive sidebar integration

State machine pattern: IDLE -> ACTIVE -> ANALYZING -> SUMMARY


Prompt Engineering Strategy

The prompts in prompts.py are designed around four principles:

1. One Question Per Call

Every prompt explicitly states "generate EXACTLY ONE question". The LLM never sees all questions at once. Each question is generated independently with the full transcript context, preventing the common failure mode of dumping all questions in a single response.

2. Forced JSON Output

All prompts end with a strict JSON schema (Respond ONLY with a valid JSON format). The Gemini API is configured with response_mime_type: 'application/json' to enforce structured output at the model level. Double protection.

3. No-Markdown Enforcement

Every prompt includes "STRICTLY NO MARKDOWN". On top of that, llm_client.py runs two sanitization passes:

  • _clean_json() - Strips markdown fences (```json) and extracts raw JSON via regex.
  • _strip_markdown() - Removes any leftover **, _, #, ~ symbols from the final text.

4. Graceful Degradation

Every generate_* function has a try/except with a meaningful hardcoded fallback:

  • generate_first_question() falls back to "What sparked your interest in {topic}?"
  • generate_next_question() falls back to a generic probing question.
  • generate_summary() falls back to a complete default summary object with all required keys.
  • The app never returns an empty or broken response to the frontend.

Tech Stack

Layer Technology Version
Frontend React 18.3
Build Vite 6.1
Language TypeScript 5.7
Styling Tailwind CSS 3.4
Backend Python + Flask 3.12 / 3.x
LLM Google Gemini 2.5 Flash
Database SQLite WAL mode, 10s timeout
Rate Limiting Flask-Limiter 200/day, 50/hr

Quick Start

Prerequisites

1. Backend Setup

cd server

# Create virtual environment
python -m venv venv
.\venv\Scripts\Activate.ps1

# Install dependencies
pip install -r requirements.txt

# Configure environment
echo "GEMINI_API_KEY=your_key_here" > .env

# Start server (port 5000)
python app.py

2. Frontend Setup

cd frontend

# Install dependencies
npm install

# Start dev server (port 5173, proxies /api to :5000)
npm run dev

3. Open the App

Navigate to http://localhost:5173. Select a topic and start your interview.


API Reference

Method Endpoint Description
GET /api/health Health check
GET /api/suggestions Get 4 AI-generated topic suggestions
POST /api/start Start interview. Body: {topic: string}
POST /api/answer Submit answer. Body: {session_id, index, answer}
POST /api/finish Generate summary. Body: {session_id}
GET /api/interviews List all completed interviews
GET /api/interview/:id Get full transcript + summary
DELETE /api/interview/:id Delete an interview (cascades)
GET /api/transcript/:id Download transcript as JSON file

Testing & Quality

The project includes a multi-layer test suite covering unit, integration, edge cases, and chaos scenarios.

cd server

# Run unit + integration tests
python -m pytest tests/test_api.py tests/test_edge_cases.py -v

# Run the full master suite (includes concurrency stress test)
python -m pytest tests/test_master_suite.py -v -k "not Frontend"

# Run E2E simulator against a live server
python tests/e2e_simulator.py

# Verify all endpoints with live Gemini
python tests/verify_all.py

What's Tested

Category Coverage
API Routes Health, start, answer, finish, transcript export
Input Validation Empty topic, too-long topic, empty answer, 3000+ char answer
State Integrity Double-submit prevention, invalid index rejection, non-existent session
LLM Resilience Gemini timeout, malformed JSON response, 429 quota fallback
Concurrency 20 simultaneous /api/start requests (SQLite WAL stress test)
E2E Flow Full interview cycle: start -> 3-5 answers -> finish -> verify transcript

Database Schema

sessions
  id TEXT PRIMARY KEY
  topic TEXT NOT NULL
  current_index INTEGER DEFAULT 0
  total_questions INTEGER DEFAULT 5
  status TEXT DEFAULT 'ACTIVE'    -- ACTIVE | FINISHED
  started_at TEXT NOT NULL

qna_pairs
  session_id TEXT NOT NULL        -- FK -> sessions.id ON DELETE CASCADE
  question_number INTEGER NOT NULL
  question TEXT NOT NULL
  answer TEXT
  asked_at TEXT NOT NULL
  answered_at TEXT

transcripts
  session_id TEXT PRIMARY KEY     -- FK -> sessions.id ON DELETE CASCADE
  topic TEXT NOT NULL
  date TEXT NOT NULL
  summary_json TEXT NOT NULL
  full_transcript TEXT NOT NULL

Project Structure

MiniAI_Interviewer/
  server/
    app.py                  # Flask routes & rate limiting
    interview_manager.py    # Business logic orchestration
    llm_client.py           # Gemini API calls & fallbacks
    prompts.py              # Prompt templates (zero logic)
    database.py             # SQLite persistence layer
    requirements.txt
    tests/
      conftest.py           # Pytest fixtures
      test_api.py           # Route-level tests
      test_edge_cases.py    # Input validation & LLM failure tests
      test_master_suite.py  # Full integration + chaos tests
      e2e_simulator.py      # Live server E2E flow
      verify_all.py         # Quick smoke test script
  frontend/
    src/
      App.tsx               # Router setup
      main.tsx              # Entry point
      pages/
        Dashboard/
          InterviewHub.tsx   # Main interview UI
        History.tsx          # Knowledge archive
      layout/
        AppLayout.tsx        # Shell with sidebar
        AppSidebar.tsx       # Collapsible navigation
      components/
        common/              # PageMeta, ScrollToTop, ComponentCard
        form/input/          # InputField, TextArea
        ui/button/           # Button component
      context/
        SidebarContext.tsx    # Sidebar state management
      icons/
        index.ts             # SVG icon components
    vite.config.ts           # Proxy config (/api -> :5000)
    tailwind.config.js
    package.json

License

Built for the Yonder technical assessment.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors