ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
Personalized AI learning roadmaps — milestones, achievement badges, and shareable progress.
Translation pending
EducationPython · JavaScriptGitHub @GibbHubbOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Turns a goal statement into a structured learning roadmap. Python/FastAPI backend with rate-limiting middleware and full API docs, React front end.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Transform your learning goals into structured, actionable roadmaps
A learning path generator that helps users master complex skills by breaking them down into structured milestones with personalized resources, time estimates, and progress tracking.
Learning complex skills is overwhelming. People struggle with:
• Where to start: Too many resources, unclear prerequisites
• How to structure learning: No clear path from beginner to proficient
• Time management: Unrealistic expectations about time commitment
• Staying motivated: Lack of visible progress and milestonesRead the full README ↗Transform your learning goals into structured, actionable roadmaps
A learning path generator that helps users master complex skills by breaking them down into structured milestones with personalized resources, time estimates, and progress tracking.
Learning complex skills is overwhelming. People struggle with:
A tool that:
┌─────────────────────────────────────────────────────────────┐
│ Frontend (React) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ LandingPage │ │ LearningPath │ │ App (State) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
HTTP/JSON (Axios)
│
┌─────────────────────────────────────────────────────────────┐
│ Backend (FastAPI) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Routes │ │ AI Service │ │ Database │ │
│ │ (REST API) │ │ (OpenAI) │ │ (SQLite) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
/api/generate/api/milestones/{id})The fastest way to run both services with a single command:
# 1. Copy the example env file and fill in your OpenAI key
cp .env.example .env
# Edit .env: set GEMINI_API_KEY=... (free key from aistudio.google.com)
# 2. Start everything
docker compose up --build
| Service | URL |
|---|---|
| Frontend | http://localhost:3000 |
| Backend | http://localhost:8000 |
| API docs | http://localhost:8000/docs |
Stop with docker compose down. The SQLite database is persisted in the db_data Docker volume.
git clone https://github.com/GibbHubb/ai-learning-path-generator.git
cd ai-learning-path-generator
cd backend
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# AP31 — the dependency list lives at the repo ROOT: Vercel's Python builder
# reads it from there and cannot follow an `-r ../file` include, so there is
# deliberately only one copy.
pip install -r ../requirements-dev.txt
# Create .env file in project root
echo "GEMINI_API_KEY=your_api_key_here" > ../.env
cd ../frontend
npm install
cd backend
python main.py
# Backend runs on http://localhost:8000
cd frontend
npm run dev
# Frontend runs on http://localhost:5173
http://localhost:5173
Every push and pull request runs .github/workflows/ci.yml:
| Job | Engine | What it runs |
|---|---|---|
backend (postgres) | Postgres 17 service container, the engine production runs | pytest backend/tests |
backend (sqlite) | SQLite file, the local-dev default | the same suite, so the two legs are comparable |
frontend | none | npm ci && npm run build |
Each backend leg first checks the collected test count against a floor (MIN_TESTS in the
workflow), because a suite that silently collects fewer tests looks exactly like one that passed.
No API key is set in CI: every test mocks the model provider.
Locally:
pip install -r requirements-dev.txt
python -m pytest backend/tests -q # SQLite (default)
# Postgres, the production engine: a throwaway local container
docker run -d --name ap-test-pg -e POSTGRES_PASSWORD=pw -p 55441:5432 postgres:17-alpine
TEST_DATABASE_URL=postgresql://postgres:[email protected]:55441/postgres python -m pytest backend/tests -q
The suite reads TEST_DATABASE_URL, never DATABASE_URL, and refuses any host that is not
localhost/127.0.0.1: it drops every table before every test, so it must never be able to reach
a real database.
backend/evals/ scores the learning-path generator against a gold set of 15 goals
(goldset.json: 6 categories, 6 languages) on five deterministic checks: the model JSON
validates against GeneratedPath; category matches; 5-8 milestones; keyword recall (the gold
keywords were written from each goal, before any response existed); estimated_hours
non-decreasing.
python backend/evals/run.py # offline: fixtures through the real /api/generate, no network
python backend/evals/run.py --live # real Gemini calls (needs GEMINI_API_KEY, asks first)
python backend/evals/run.py --record # --live, then overwrite fixtures/ with the real replies
python backend/evals/run.py --write-baseline # after an intended change: re-pin baseline.json
backend/tests/test_evals_ap39.py fails if an offline score drops below baseline.json.
Read the number honestly:
--live
does that. Every run prints the fixtures' source and date beside the score."source": "hand-authored"): no API
key was available when the harness was built. They were written by the same person who wrote
the keywords, so keyword recall on them is 1.00 by construction and says nothing about the
model. Run --record once with a key to replace them.ai_service.py: bump PROMPT_VERSION, run --record, review
the table, then --write-baseline.Enter your learning goal
Select experience level
Set time commitment
Generate path
Explore your path
Generate new paths
POST /api/generateGenerate a new learning path
Request Body:
{
"goal": "Learn Full-Stack Web Development",
"experience_level": "beginner",
"time_commitment": "10-20 hours/week"
}
Response:
{
"id": 1,
"title": "Full-Stack Web Development Mastery",
"description": "A comprehensive path...",
"experience_level": "beginner",
"time_commitment": "10-20 hours/week",
"created_at": "2024-01-22T12:00:00",
"milestones": [
{
"id": 1,
"title": "HTML & CSS Fundamentals",
"description": "Learn the building blocks...",
"order": 0,
"estimated_hours": 15,
"resources": ["MDN Web Docs", "freeCodeCamp"],
"completed": false,
"completed_at": null
}
]
}
GET /api/pathsGet all learning paths
GET /api/paths/{path_id}Get a specific learning path
PATCH /api/milestones/{milestone_id}Update milestone completion status
Request Body:
{
"completed": true
}
DELETE /api/paths/{path_id}Delete a learning path
Visit http://localhost:8000/docs for Swagger UI documentation
Path generation uses Server-Sent Events (SSE) via POST /api/generate/stream.
/api/generate/stream.data: SSE event as soon as the full response is available.event: done carries the complete LearningPathResponse JSON — the frontend switches to the path view with zero extra requests.POST /api/generate is untouched and continues to work.data: {"id": 1, "title": "...", "order": 0, ...}
data: {"id": 2, "title": "...", "order": 1, ...}
event: done
data: {"id": 7, "title": "Full path title", "milestones": [...], ...}
The progressive milestone list is rendered live in the generation form so users can see the path being built in real time.
Every path and its completion state are stored in SQLite.
GET /api/paths and, if a previous path exists, shows a "Resume your learning path on [title]?" banner with Continue / Start new buttons.PATCH /api/milestones/{id} which sets completed and completed_at on the database row.Focus on Learning Specifically
AI Prompt Engineering
Progress Tracking Over Collaboration
Monorepo Structure
SQLite for MVP
Vanilla CSS Over Tailwind
React State Over Redux
GPT-4 Mini for Cost Efficiency
Dark Theme
Glassmorphism
Timeline Visualization
Expandable Milestones
generation_cache DB table replaces the old in-process dict (which almost never
hit on Vercel — a fresh, empty dict per recycled function instance). Keyed on a
SHA-256 of the normalised (goal, experience_level, time_commitment, language, model) tuple, so casing/whitespace variants of the same goal collide and a model
change never silently serves stale output. Only a schema-valid result is ever
cached; entries expire after CACHE_TTL_DAYS (default 30, see .env.example).
Also closed the streaming endpoint's double provider call (/api/generate/stream
used to call the generator AND the plain generator again for the header fields)..env.example
(TRUSTED_PROXY_HEADER) and backend/rate_limit.py.llm.chat_json call is capped (max_tokens, timeout) and recorded, win or fail,
in model_calls. A DAILY_CALL_BUDGET env var caps total calls per day — once spent,
the model-calling routes 503 without ever constructing the provider client. See
.env.example (DAILY_CALL_BUDGET), backend/usage.py, and the CRON_SECRET-gated
GET /api/admin/usage for today's per-model counts.Total Time: ~6 hours
Planning & Architecture (45 min)
Backend Development (1.5 hours)
Frontend Development (2.5 hours)
Integration & Testing (45 min)
Documentation (45 min)
Chose to prioritize:
Deferred for v2:
MIT License - feel free to use this project for learning or inspiration!
Max