ESP32-S3 Bench Guide
ESP32‑S3 N16R8 벤치 가이드: 실시간 핀아웃, 강의, 시뮬레이터 및 벤치 로그
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Turns a goal statement into a structured learning roadmap. Python/FastAPI backend with rate-limiting middleware and full API docs, React front end.
저장소 README에서 가져옴
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:
• 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 milestonesREADME 전문 보기 ↗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