Skip to content

gravitypersists/verno

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

208 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Verno

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.


What It Does

  • 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 prereq and related edges 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.

Why It Is Interesting

Most AI tutoring demos treat the lesson as a single conversation. Verno treats learning as a stateful system:

  1. The course has structure. Generated courses are not just markdown outlines. They are persisted as database-backed concept graphs with areas, topics, and edges.
  2. The tutor has context. Tutor prompts receive the current topic, surrounding topics, prior lesson steps, conversation history, and learner proficiency data.
  3. The UI follows the learning path. The course home can suggest the first lesson, resume an unfinished lesson, or recommend the next topic.
  4. Long-running AI operations are first-class. LLM calls are wrapped in an Effect-based run bus so the frontend can subscribe to a runId and show live progress.
  5. Contracts are shared end-to-end. The frontend and backend both use the @verno/contracts package for Zod schemas and TypeScript types.

Product Walkthrough

1. Start With a Learning Goal

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

2. Generate a Course

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.

3. Browse and Join Courses

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.

4. Learn Topic by Topic

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

5. Track Proficiency

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.

Architecture

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

Frontend

  • 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 /api proxied 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

Backend

  • 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-v2 for 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 models

Shared Contracts

The 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.

Data Model Highlights

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.

Streaming Model

Long-running operations such as course generation and tutor replies follow the same pattern:

  1. The client creates a runId.
  2. The client opens an SSE connection to /api/stream/:runId.
  3. The client triggers the operation.
  4. The backend publishes events to an Effect PubSub.
  5. The UI responds to started, progress, delta, done, error, and contextUpdated events.

This makes generated courses and tutor replies feel interactive even when the underlying LLM work takes time.

Getting Started

Prerequisites

  • Node.js
  • Yarn
  • Docker, for the default backend development workflow
  • An OpenRouter API key
  • OAuth app credentials for GitHub and/or Google login

Install Dependencies

From the repository root:

yarn install

Configure Environment

Create 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:5173

The Docker Compose setup provides DATABASE_URL for the API container. If you run the backend outside Docker, set DATABASE_URL yourself.

Run the Backend

From server/:

yarn dev

This 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:studio

Run the Frontend

From ui/:

yarn dev

The Vite app runs on port 5173 and proxies /api requests to http://localhost:3000.

Build the Frontend

From ui/:

yarn build

In production mode, the Fastify server serves the built UI from ui/dist.

API Surface

The main API routes are:

  • POST /api/courses - create a course from learner intent
  • GET /api/courses - list visible courses
  • GET /api/course/:id - fetch a course and its graph
  • PATCH /api/course/:id/publish - publish or unpublish a course
  • POST /api/courses/:courseId/join - enroll in a course
  • GET /api/me/courses - list the current user's enrollments
  • GET /api/me/courses/:courseId/predictNext - recommend the next topic
  • POST /api/courses/:courseId/conversations - create a tutor conversation
  • POST /api/conversations/:conversationId/walk - advance through a lesson
  • POST /api/conversations/:conversationId/messages - ask a free-form tutor question
  • GET /api/stream/:runId - subscribe to LLM run events
  • GET /api/knowledge/:enrollmentId - inspect learner knowledge state

Future Goals

  • richer quizzes
  • course editing
  • third-party content ingestion
  • stronger authorization patterns -
  • more advanced learner modeling.

About

A prototype tutoring wrapper around LLMs

Resources

Stars

Watchers

Forks

Contributors