Verno is an AI-powered learning platform that turns a learner's goal into a structured, adaptive course. A user can describe what they want to learn, choose the kind of learning experience they prefer, and Verno generates a course made of topic graphs, prerequisite relationships, personalized lessons, and tutor conversations.
For example: "I am an experienced SWE and TypeScript expert, teach me Rust."
The project explores what an AI tutor can be when it is more than a chat box: Verno keeps a model of the course, the learner's enrollment, their topic-level proficiency, and their progress through each lesson so the tutor can guide the learner through a coherent path instead of answering one-off questions.
I am no longer working on this project so it serves only as a sample and maybe inspiration for whoever happens upon it. Please be mindful that code here might not necessarily represent what I would consider production quality code, and is more of a high quality prototype.
- Builds courses from learner intent. The course builder asks for a subject, goals, focus areas, prior experience, and preferred learning style, then uses an LLM to generate a complete course.
- Represents curriculum as graphs. Courses are stored as area graphs and topic graphs, with
prereqandrelatededges that describe how concepts connect. - Creates personalized lesson plans. Each learner gets lesson plans for a topic based on the course graph, nearby concepts, and their current proficiency profile.
- Supports multiple teaching modalities. Lessons can be lecture-style, inquiry-based, Socratic, open-response, or Feynman-style challenges.
- Streams AI work in real time. Course generation and tutor responses emit progress, deltas, completion, and error events over server-sent events.
- Tracks learner state. Enrollments, conversations, lesson cursors, next actions, and topic proficiencies are persisted so learners can continue where they left off.
- Includes course publishing and admin views. Course creators can publish generated courses, and admins can inspect tutor conversations and course state.
Most AI tutoring demos treat the lesson as a single conversation. Verno treats learning as a stateful system:
- The course has structure. Generated courses are not just markdown outlines. They are persisted as database-backed concept graphs with areas, topics, and edges.
- The tutor has context. Tutor prompts receive the current topic, surrounding topics, prior lesson steps, conversation history, and learner proficiency data.
- The UI follows the learning path. The course home can suggest the first lesson, resume an unfinished lesson, or recommend the next topic.
- Long-running AI operations are first-class. LLM calls are wrapped in an Effect-based run bus so the frontend can subscribe to a
runIdand show live progress. - Contracts are shared end-to-end. The frontend and backend both use the
@verno/contractspackage for Zod schemas and TypeScript types.
The landing page asks what the user wants to learn. From there, the course builder collects:
- the subject
- desired outcomes
- custom goals
- prior experience
- relevant background
- a preferred learning style, such as structured curriculum, quick overview, fun and engaging, or hands-on projects
The backend sends the learner intent to the tutor agent through OpenRouter. The generated result is parsed through structured Zod schemas and persisted as:
- course metadata
- area nodes
- topic nodes
- prerequisite and related edges
- course parameter settings such as cognitive load, concept density, narrative potential, and style notes
- optional course imagery metadata
The API returns immediately with a runId, while the UI listens to /api/stream/:runId for generation progress.
Users can browse published courses, open a course home page, enroll, and view the generated curriculum structure. A course tracks ownership, publishing state, soft deletion, cover imagery, enrollments, and learner-specific progress.
Opening a topic creates or resumes a tutor conversation. The conversation context stores the topic, lesson cursor, available next actions, and whether the topic lesson is complete. As the learner proceeds, Verno can:
- construct a personalized lesson plan
- stream the tutor's next response
- offer primary actions such as "Let's get started" or "Continue"
- allow free-form learner questions when the current lesson step supports it
- persist tutor and user messages
Verno stores topic-level proficiency values per enrollment. The grader service can estimate initial proficiency, expose current knowledge state, and predict the next topic to study. The data model leaves room for more advanced knowledge tracing approaches by storing algorithm-specific parameters as JSON.
Verno is a Yarn workspace monorepo with three packages:
verno/
|-- contracts/ # Shared Zod schemas and TypeScript API contracts
|-- server/ # Fastify API, Prisma data layer, tutor agents, SSE streams
`-- ui/ # React app, course builder, course viewer, chat UI, admin views- React 19
- React Router 7
- TanStack Query
- Tailwind CSS 4
- Radix UI / shadcn-style components
- React Markdown with math, GFM, syntax highlighting, and heading plugins
- Vite, with
/apiproxied to the backend during development
Key frontend areas:
ui/src/app/routes/LandingPage/ # Entry point and learning-goal prompt
ui/src/app/routes/CourseBuilder/ # Multi-step course creation wizard
ui/src/app/routes/Courses/ # Course discovery and enrolled courses
ui/src/app/routes/Course/ # Course home, topic routes, sidebar, breadcrumbs
ui/src/app/routes/Conversation/ # Tutor conversation screen and streaming behavior
ui/src/app/routes/Admin/ # Admin conversation and course inspection
ui/src/components/chat/ # Reusable chat primitives
ui/src/api/ # API client, hooks, query keys, stream hooks- Fastify 5
- Prisma with PostgreSQL
- Secure cookie sessions
- GitHub and Google OAuth
- OpenRouter for LLM calls
- Effect for async workflows and pub/sub
fastify-sse-v2for server-sent events- Eta templates for tutor prompts
- Zod structured outputs for LLM responses
Key backend areas:
server/src/server.ts # Fastify app setup and route registration
server/src/routes/ # Courses, conversations, auth, stream, knowledge, admin
server/src/services/ # Course, conversation, enrollment, grading services
server/src/repos/ # Prisma-backed persistence helpers
server/src/agents/ # OpenRouter client, tutor API, prompt rendering
server/src/agents/tutor/prompts/ # Eta prompt templates
server/prisma/schema.prisma # Auth, course graph, lesson, conversation, proficiency modelsThe contracts package contains Zod schemas for courses, conversations, enrollments, messages, streams, users, and common route parameters. This keeps API payloads consistent across the Fastify server and React client.
The Prisma schema is centered around a few core concepts:
- Users and identities for GitHub/Google login and admin access.
- Courses with generated area nodes, topic nodes, and graph edges.
- Enrollments that connect users to courses.
- Conversations with messages and typed conversation context.
- Personalized lessons with ordered lesson steps and teaching modalities.
- Topic proficiencies that store normalized mastery scores and grading metadata.
Long-running operations such as course generation and tutor replies follow the same pattern:
- The client creates a
runId. - The client opens an SSE connection to
/api/stream/:runId. - The client triggers the operation.
- The backend publishes events to an Effect
PubSub. - The UI responds to
started,progress,delta,done,error, andcontextUpdatedevents.
This makes generated courses and tutor replies feel interactive even when the underlying LLM work takes time.
- Node.js
- Yarn
- Docker, for the default backend development workflow
- An OpenRouter API key
- OAuth app credentials for GitHub and/or Google login
From the repository root:
yarn installCreate server/.env with the values needed by the API:
ENVIRONMENT=development
SESSION_SECRET=replace-with-a-long-random-secret
OPENROUTER_API_KEY=your-openrouter-key
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
GITHUB_REDIRECT_URI=http://localhost:3000/api/auth/github/callback
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_REDIRECT_URI=http://localhost:3000/api/auth/google/callback
WEB_ORIGIN=http://localhost:5173The Docker Compose setup provides DATABASE_URL for the API container. If you run the backend outside Docker, set DATABASE_URL yourself.
From server/:
yarn devThis starts PostgreSQL, the Fastify API on port 3000, and Prisma Studio on port 5555.
Useful backend commands:
yarn prisma:migrate
yarn db:seed
yarn db:reset
yarn prisma:studioFrom ui/:
yarn devThe Vite app runs on port 5173 and proxies /api requests to http://localhost:3000.
From ui/:
yarn buildIn production mode, the Fastify server serves the built UI from ui/dist.
The main API routes are:
POST /api/courses- create a course from learner intentGET /api/courses- list visible coursesGET /api/course/:id- fetch a course and its graphPATCH /api/course/:id/publish- publish or unpublish a coursePOST /api/courses/:courseId/join- enroll in a courseGET /api/me/courses- list the current user's enrollmentsGET /api/me/courses/:courseId/predictNext- recommend the next topicPOST /api/courses/:courseId/conversations- create a tutor conversationPOST /api/conversations/:conversationId/walk- advance through a lessonPOST /api/conversations/:conversationId/messages- ask a free-form tutor questionGET /api/stream/:runId- subscribe to LLM run eventsGET /api/knowledge/:enrollmentId- inspect learner knowledge state
- richer quizzes
- course editing
- third-party content ingestion
- stronger authorization patterns -
- more advanced learner modeling.