File size: 4,954 Bytes
6a8e2e2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# AI InterviewMentor β€” Architecture Overview

## System Architecture

```
Browser (React SPA)
    β”‚
    β–Ό
HF Spaces Docker Container (port 7860)
β”œβ”€β”€ FastAPI (Python 3.11)
β”‚   β”œβ”€β”€ /api/auth/*        β†’ Custom JWT auth (jose + bcrypt)
β”‚   β”œβ”€β”€ /api/batches/*     β†’ Batch/class management
β”‚   β”œβ”€β”€ /api/topics/*      β†’ Topic CRUD + unlock control
β”‚   β”œβ”€β”€ /api/upload        β†’ CSV question bank ingestion
β”‚   β”œβ”€β”€ /api/sessions/*    β†’ Session detail + reports
β”‚   β”œβ”€β”€ /api/instructor/*  β†’ Dashboard + student analytics
β”‚   β”œβ”€β”€ /api/student/*     β†’ Student dashboard
β”‚   β”œβ”€β”€ /interview/*       β†’ LangGraph interview engine
β”‚   └── / (catch-all)      β†’ React static files (Vite build)
β”‚
β”œβ”€β”€ LangGraph Engine
β”‚   β”œβ”€β”€ ask_question       β†’ Picks next question from bank
β”‚   β”œβ”€β”€ evaluate_answer    β†’ Scores: strong | shallow | wrong
β”‚   β”œβ”€β”€ counter_question   β†’ Probing follow-up for shallow answers
β”‚   β”œβ”€β”€ summarize          β†’ Compresses conversation every 4 turns
β”‚   └── generate_report    β†’ Final scored feedback JSON
β”‚
└── External Services
    β”œβ”€β”€ NeonDB (PostgreSQL)
    β”‚   β”œβ”€β”€ public schema  β†’ App data (users, batches, topics, questions, sessions)
    β”‚   └── checkpointer   β†’ LangGraph state (auto-managed)
    └── OpenRouter API     β†’ MiniMax 2.7 model
```

## Tech Stack

| Layer | Technology |
|-------|-----------|
| Frontend | React 18 + Vite + TypeScript + Tailwind CSS v3 (dark theme only) |
| Backend | FastAPI (Python 3.11) |
| AI Orchestration | LangGraph (state machine with checkpointing) |
| Database | NeonDB (PostgreSQL) β€” app data + LangGraph checkpoints |
| AI Gateway | OpenRouter β†’ MiniMax 2.7 |
| Auth | Custom JWT (jose + bcrypt) β€” no third-party auth |
| Deployment | Single Docker container on Hugging Face Spaces (port 7860) |

## Key Design Decisions

1. **Single container deployment** β€” React static build served by FastAPI, no split deployment
2. **Same-origin API calls** β€” No CORS needed, browser hits FastAPI directly
3. **Two DB schemas** β€” `public` for app data, `checkpointer` for LangGraph state
4. **Token budget control** β€” Summarize every 4 turns, ~700 token ceiling per LLM call
5. **session_id = thread_id** β€” Natural scoping between DB sessions and LangGraph state
6. **CSV-to-DB question ingestion** β€” No file storage needed (no S3/R2)
7. **Custom JWT only** β€” Supabase/Firebase explicitly banned (hackathon rules)

## Database Schema

### Tables

- **users** β€” id, full_name, email, password_hash, role (student|instructor), batch_id
- **refresh_tokens** β€” id, user_id, token_hash, expires_at
- **batches** β€” id, name, instructor_id, class_code (auto-generated)
- **topics** β€” id, batch_id, name, is_unlocked, order_index
- **questions** β€” id, topic_id, question_text, difficulty (easy|medium|hard)
- **interview_sessions** β€” id, student_id, topic_id, status, score, feedback (JSONB)

### Relationships

```
instructor (user) β†’ creates batches β†’ has topics β†’ has questions
student (user) β†’ joins batch via class_code β†’ takes interview_sessions on topics
```

## Auth Flow

- **Access token**: HS256 JWT, 15min expiry, sent in `Authorization: Bearer` header
- **Refresh token**: 7-day expiry, bcrypt-hashed in DB, sent as httpOnly cookie
- **Route protection**: `get_current_user` dependency extracts JWT β†’ `require_instructor` / `require_student` role guards

## LangGraph Interview Flow

```
START β†’ ask_question β†’ [wait for student answer] β†’ evaluate_answer
                                                        β”‚
                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                              β–Ό                          β–Ό              β–Ό
                       counter_question            ask_question    generate_report β†’ END
                              β”‚                    (or summarize
                              β–Ό                     every 4 turns)
                       evaluate_answer
```

- **8 turns max** or question bank exhausted β†’ generate_report
- **Counter-questions** fire once per shallow answer (no double-countering)
- **Summarize node** compresses messages every 4 turns to keep token usage flat

## Frontend Architecture

- **Routing**: React Router with `ProtectedRoute` wrapper
- **State**: Zustand stores β€” `authStore` (JWT + user), `interviewStore` (session state)
- **API layer**: Single `apiFetch` wrapper with auto-refresh on 401
- **Pages**: Login, Signup, StudentDashboard, Interview, Report, InstructorDashboard, Upload, StudentDetail

## Scope Boundary (Not In V1)

Voice input, email notifications, leaderboard, certificates, question bank editor UI, light theme, multi-instructor batches.