Skip to content

Latest commit

 

History

History
174 lines (107 loc) · 9.39 KB

File metadata and controls

174 lines (107 loc) · 9.39 KB

LearnLoop Revamp Decisions

This file records architecture and product decisions made during the revamp. Keep each entry short, concrete, and tied to implementation evidence.

Current Baseline

  • The backend started as a single Flask app in backend/thinkmate.py.
  • Day 1 split the backend into a minimal Flask package under backend/app/ and renamed the stale backend entry point to backend/main.py.
  • The frontend uses React 19, Vite, Vitest, and Wouter.
  • Persistence is currently SQLAlchemy with SQLite fallback and optional Supabase URI.
  • Local SQLite stores conversation, quiz, and flashcard records as untracked runtime data.
  • Day 2 adds session-scoped in-memory FAISS retrieval with local all-MiniLM-L6-v2 embeddings.
  • Locust evidence now includes a fixed 500-user local Gunicorn run. Quiz and flashcard generation uses Pydantic validation with bounded repair retries.

Decisions

1. Stabilize the backend before redesigning the frontend

Decision: Phase 1 will focus on backend structure, testability, Docker correctness, and SQLite WAL before adding new user-facing workflows.

Reason: The resume claims depend on backend behavior. A frontend redesign before real RAG, validation, and load testing would make the app look better without making the technical claims true.

2. Keep local development privacy-first

Decision: The RAG layer will use local all-MiniLM-L6-v2 embeddings and in-memory FAISS indexes scoped by session.

Reason: This directly supports the privacy and session-isolation claim. It also keeps retrieval fast and avoids sending raw study material to a vector database service.

3. Measure before claiming benchmark numbers

Decision: Recall@5, retrieval latency, malformed JSON rate, and load-test results must be measured and saved before appearing in README or resume wording.

Reason: The project should exceed the existing resume claims with traceable evidence, not approximate or fabricated numbers.

4. Replace regex-only JSON cleanup with schema validation

Decision: Quiz and flashcard generation will use Pydantic schemas plus a bounded repair loop.

Reason: Current regex cleanup handles only simple formatting problems. Schema validation is more defensible and lets us report actual malformed-output recovery behavior.

5. Defer full frontend redesign until backend workflows are real

Decision: Phase 6 is the complete frontend redesign. Earlier phases may add small UI hooks only when needed to test backend workflows.

Reason: The final interface should be designed around the real study workspace, source-grounded answers, quiz generation, flashcards, and analytics.

6. Add a feature-expansion phase after the core claims are real

Decision: Phase 8 will add additional high-value product features after the baseline revamp, RAG, validation, load testing, redesign, and production deployment phases are complete.

Reason: Extra features should build on stable and deployed foundations. We will brainstorm exact Phase 8 scope later, but candidates include adaptive review, exports, source citations, topic mastery tracking, and shareable study packs.

7. Work directly on main for this revamp

Decision: Subsequent commits should be made directly on main and pushed to origin/main; do not create feature branches unless explicitly requested later.

Reason: The user wants a seamless linear workflow for this project instead of branch-and-merge coordination.

8. Use logical multi-commit history with explanatory messages

Decision: Keep commits logically separate and use commit messages with a concise subject plus a short body explaining what changed and why.

Reason: Separate commits make review and rollback easier. The body should preserve the reasoning behind each change without making the subject line noisy.

9. Use main.py as the backend entry point

Decision: The backend now uses backend/app/ for factory, models, routes, and services, with backend/main.py as the executable entry point.

Reason: thinkmate.py was stale project branding. main.py is a standard backend entry-point name and keeps Docker/local startup aligned with the LearnLoop project name.

10. Deploy the core app before feature expansion

Decision: Phase 7 will make the completed core app deployment-compatible and deploy it before Phase 8 product work starts.

Reason: Render, Vercel, and any separate model service must be validated against the real RAG and load-test architecture before additional features expand the deployment surface.

11. Preserve 512-token retrieval chunks across MiniLM windows

Decision: RAG chunks use the MiniLM tokenizer for 512-token boundaries with 64-token overlap. Embeddings are pooled across model-sized windows so all chunk tokens contribute despite the model's 256-token sequence limit.

Reason: Sending a 512-token chunk directly to all-MiniLM-L6-v2 silently truncates content after its usable sequence limit and weakens retrieval for information near the end of the chunk.

12. Make Day 6 a complete frontend vertical slice

Decision: Day 6 includes the minimum backend work required for persistent study sessions, material management, session-grounded flashcards, progress, history, and an isolated demo journey.

Reason: A frontend that displayed recent sessions, durable materials, or session-based practice without real persistence would misrepresent product behavior.

13. Scope visitors without implying authentication

Decision: Guest mode creates a random browser identifier and sends it with study requests. Guest records are scoped to that identifier, while authenticated requests use the verified Supabase user identity.

Reason: Public demo visitors need isolated state, and account users need a separate identity-backed path.

14. Preserve benchmark reports as static evidence

Decision: The Benchmarks page renders checked-in measured results instead of adding a runtime benchmark API.

Reason: Benchmark data changes only when a deliberate evaluation run updates its report. A live endpoint would add infrastructure without improving evidence quality.

15. Replace the legacy CRA toolchain

Decision: The Day 6 frontend uses Vite and Vitest, with Wouter for the small client-side route surface.

Reason: Create React App and its transitive dependencies produced unresolved security advisories and stale build warnings. The smaller toolchain preserves the existing routes while keeping the production dependency audit clean.

16. Keep runtime SQLite data out of Git

Decision: backend/conversations.db is local runtime state and is ignored by Git. Docker Compose stores its SQLite database in the learnloop-data named volume.

Reason: Visitor sessions and generated study artifacts are mutable data, not source code. A named volume preserves local Docker data without publishing a developer database.

17. Add accounts without storing source files permanently

Decision: Supabase Auth provides email/password accounts. Authenticated requests carry a verified Supabase bearer token, while guest mode remains browser-session scoped. The Supabase schema stores account-owned learning activity but does not include a durable source table.

Reason: Users need saved history and scores without making uploaded PDFs or pasted source material permanent server-side data.

18. Treat PDF uploads as complete session sources

Decision: A PDF is treated as one source, extracted as selectable text, and chunked using the existing session retrieval pipeline. Users can add a PDF and pasted text to the same learning space.

Reason: This keeps the original source workflow simple while allowing real study documents without requiring a separate document-management system.

19. Start production with one Render worker and an embedding provider boundary

Decision: The first hosted backend uses local MiniLM in-process with one Render worker. The RAG service now selects either this local provider or an HTTP embedding provider through EMBEDDING_PROVIDER.

Reason: A remote Modal service adds another deployment, authentication, timeout, cold-start, and failure dependency before the core app has a verified public path. One worker avoids duplicating the model and keeps its in-memory FAISS index coherent within a process. The HTTP boundary preserves a migration path when Render memory, model download time, or scale-out requirements justify Modal.

20. Move all production and local RAG inference to Modal

Decision: LearnLoop no longer supports local MiniLM or FAISS as an application runtime. Modal owns document chunking and all-MiniLM-L6-v2 embeddings. Tests use a deterministic fake provider instead of downloading the model.

Reason: The Render Free service already hosts other portfolio projects and the in-process model caused unacceptable startup and memory pressure. Removing the model and FAISS dependencies makes the API process smaller and keeps the deployed architecture identical to the tested remote-provider path.

21. Persist chunks and vectors in Supabase pgvector

Decision: The API stores source chunks and 384-dimensional embeddings in Supabase pgvector and retrieves them with cosine-distance search. Session indexes are no longer process memory and do not reset after restart or scale-out.

Reason: Modal solves model placement, not data persistence. Durable vectors are required for retrieval to remain available after Render restarts. Persisting chunks also changes source retention from the earlier session-only design and must remain visible in the privacy and deployment documentation.