Skip to content

Repository files navigation

OpenHours 🎓

Devpost page

AI-powered office hours — scoped to your course, built to guide not replace.

OpenHours lets professors upload their course materials (PDFs, slides, lecture videos) and gives students an AI assistant that answers questions strictly based on that content. The AI hints and guides rather than just giving answers.

Built for the Intellectual Pursuit track.


The Idea

Professors answer the same questions over and over, students wait days for responses, and generic AI tools give answers that have nothing to do with the course. OpenHours fixes this by:

  • Giving students instant, scoped AI help 24/7
  • Freeing professors from repetitive questions
  • Keeping AI constrained to course content (no hallucinations, no cheating shortcuts)
  • Showing professors what topics students struggle with most

Not replacing professors. Streamlining intellectual pursuit.


Maintainers

OpenHours is owned and maintained jointly by three maintainers in equal undivided one-third (1/3) shares. Routine work can be done by any one of us; material decisions about the project (licensing, sale, policy changes, sunsetting, fundraising, brand, etc.) require unanimous consent. See LICENSE for the full governance terms.

MaintainerShareRole
Emilio Scott1/3Co-owner, joint decision-maker
Colin Mcdonald1/3Co-owner, joint decision-maker
Elias Santillan1/3Co-owner, joint decision-maker

Contact: legal@openhours.me (legal), privacy@openhours.me (privacy), security@openhours.me (security). Mail to any of these is deemed received by all three maintainers.


Tech Stack

LayerTechnology
FrontendNext.js 16 (App Router), React 19, Tailwind CSS v4, Framer Motion, lucide-react
BackendFastAPI (Python)
AI PipelineOpenAI API (embeddings: text-embedding-3-small, chat: gpt-4o-mini) — LangChain is used only for text chunking
File ParsingPyPDF, python-pptx, OpenAI Whisper (optional, install separately)
DatabaseSupabase (PostgreSQL + pgvector)
File StorageSupabase Storage
AuthSupabase Auth (professor / student roles in profiles table)
DeploymentVercel (frontend), Railway (backend)

Repository Structure

openhours/
├── frontend/ # Next.js app
│ ├── app/
│ │ ├── page.tsx # Landing page
│ │ ├── layout.tsx
│ │ ├── globals.css
│ │ ├── not-found.tsx # Custom 404
│ │ ├── icon.png # Favicon
│ │ ├── auth/
│ │ │ ├── login/page.tsx
│ │ │ ├── signup/page.tsx
│ │ │ └── verify/page.tsx # Email verification holding page
│ │ ├── student/
│ │ │ └── page.tsx # Student chat UI + sidebar of past sessions
│ │ ├── professor/
│ │ │ ├── page.tsx # Professor dashboard
│ │ │ ├── upload/page.tsx # Upload course materials + create courses
│ │ │ └── analytics/page.tsx
│ │ └── api/ # Next.js API routes (proxy to FastAPI)
│ │ ├── ask/route.ts
│ │ ├── upload/route.ts
│ │ └── analytics/route.ts
│ ├── lib/
│ │ ├── supabase.ts # Browser-only Supabase client
│ │ └── utils.ts # `cn()` className helper
│ ├── public/ # Logo assets
│ ├── package.json
│ └── .env.example
│
├── backend/ # FastAPI (Python)
│ ├── main.py # All API endpoints
│ ├── services/
│ │ ├── parser.py # PDF / PPTX / video parsing
│ │ ├── embeddings.py # Chunking + OpenAI embeddings
│ │ ├── rag.py # RAG pipeline (retrieval + chat completion)
│ │ └── analytics.py # GPT-based question clustering
│ ├── requirements.txt
│ └── .env.example
│
├── supabase/
│ └── schema.sql # DB schema + pgvector setup + RLS policies
│
├── DEPLOYMENT.md # Vercel + Railway + Supabase deployment guide
├── CLAUDE.md # Guidance for AI coding assistants
└── README.md

Getting Started

Prerequisites


1. Clone the repo

git clone https://github.com/your-team/openhours.git
cd openhours

2. Set up Supabase

  1. Create a new project at supabase.com
  2. Go to Database → Extensions and enable vector
  3. Go to SQL Editor and run the contents of supabase/schema.sql
  4. Grab your project URL and anon key from Settings → API

3. Environment Variables

Copy the example env files and fill in your keys:

Frontend:

cd frontend
cp .env.example .env
# SupabaseNEXT_PUBLIC_SUPABASE_URL=your_supabase_urlNEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key# Backend URLFASTAPI_URL=http://localhost:8000

Backend:

cd backend
cp .env.example .env
# SupabaseSUPABASE_URL=your_supabase_urlSUPABASE_SERVICE_ROLE_KEY=your_service_role_key# OpenAIOPENAI_API_KEY=your_openai_key# Frontend URL (for CORS)FRONTEND_URL=http://localhost:3000

4. Run the Frontend

cd frontend
npm install
npm run dev

Frontend runs at http://localhost:3000


5. Run the Backend

cd backend
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
uvicorn main:app --reload

Backend runs at http://localhost:8000


Database Schema

Canonical source: supabase/schema.sql. The snippet below is the core data model for quick reference. This snippet omits some production details such as RLS policies, secondary indexes, and the unused bookings table; use supabase/schema.sql for the full schema.

-- Enable pgvector extension
create extension if not exists vector;
-- Profiles (extends Supabase auth.users)createtableif not exists profiles (
id uuid referencesauth.users(id) on delete cascadeprimary key,
role textnot nullcheck (role in ('professor', 'student')),
full_name text,
created_at timestamptz default now()
);
-- Auto-create a profile row when a new user signs upcreate or replacefunctionhandle_new_user()
returns trigger language plpgsql security definer as $$
begininsert intopublic.profiles (id, role, full_name)
values (
new.id,
coalesce(new.raw_user_meta_data->>'role', 'student'),
coalesce(new.raw_user_meta_data->>'full_name', '')
)
on conflict (id) do nothing;
return new;
end;
$$;
droptrigger if exists on_auth_user_created onauth.users;
createtriggeron_auth_user_created
after insert onauth.users
for each row execute procedure handle_new_user();
-- Coursescreatetableif not exists courses (
id uuid primary key default gen_random_uuid(),
professor_id uuid references profiles(id) on delete cascadenot null,
name textnot null,
description text,
created_at timestamptz default now()
);
createindexif not exists courses_professor_id_idx on courses(professor_id);
-- Documents (parsed chunks + embeddings)createtableif not exists documents (
id uuid primary key default gen_random_uuid(),
course_id uuid references courses(id) on delete cascadenot null,
content textnot null,
embedding vector(1536), -- OpenAI text-embedding-3-small dimensions
source_file text,
created_at timestamptz default now()
);
createindexif not exists documents_course_id_idx on documents(course_id);
-- Chat sessions (student conversation history shown in sidebar)createtableif not exists chat_sessions (
id uuid primary key default gen_random_uuid(),
student_id uuid references profiles(id) on delete cascadenot null,
course_id uuid references courses(id) on delete cascadenot null,
title textnot null,
pinned booleannot null default false,
created_at timestamptz default now(),
updated_at timestamptz default now()
);
-- Chat messages (one row per turn within a session)createtableif not exists chat_messages (
id uuid primary key default gen_random_uuid(),
session_id uuid references chat_sessions(id) on delete cascadenot null,
role textnot nullcheck (role in ('user', 'assistant')),
content textnot null,
created_at timestamptz default now()
);
-- Question logs (for analytics)createtableif not exists question_logs (
id uuid primary key default gen_random_uuid(),
course_id uuid references courses(id) on delete cascadenot null,
question textnot null,
created_at timestamptz default now()
);
createindexif not exists question_logs_course_id_idx on question_logs(course_id);
createindexif not exists question_logs_created_at_idx on question_logs(created_at desc);
-- Semantic search functioncreate or replacefunctionmatch_documents(
query_embedding vector(1536),
match_course_id uuid,
match_count int default 6
)
returns table (content text, similarity float)
language sql stable
as $$
select
content,
1- (embedding <-> query_embedding) as similarity
from documents
where course_id = match_course_id
and embedding is not nullorder by embedding <-> query_embedding
limit match_count;
$$;

How the RAG Pipeline Works

RAG = Retrieval Augmented Generation. The AI only answers based on what the professor uploaded.

File Ingestion (professor uploads)

Professor uploads file
↓
Store raw file in Supabase Storage
↓
Parse text from file:
PDF → PyPDF
PPTX → python-pptx
A/V (opt) → openai-whisper (install separately; not in requirements.txt)
↓
Split text into chunks (~500 tokens, 50 overlap, via LangChain text splitter)
↓
Convert each chunk to vector embedding (OpenAI text-embedding-3-small)
↓
Store chunks + embeddings in Supabase pgvector

Student Query

Student asks a question
↓
Convert question to vector embedding
↓
Search pgvector for closest matching chunks
↓
Feed top chunks as context to AI
↓
AI answers ONLY based on that context

Key API Endpoints (FastAPI)

MethodEndpointDescription
GET/healthHealth check (returns {"status": "ok"})
POST/uploadAccept file, parse, embed, store in pgvector
POST/askTake student question, search pgvector, return AI answer
GET/analytics/{course_id}Return most common question topics
POST/book(Unused.) Creates a row in bookings — no UI exposes this

Deployment

Frontend → Vercel

cd frontend
npx vercel

Backend → Railway

  1. Push to GitHub
  2. Connect repo to railway.app
  3. Set environment variables in Railway dashboard
  4. Deploy

Build Order (Hackathon Timeline)

Built jointly by the three maintainers.

StepTask
1Supabase setup, enable pgvector, run schema
2Next.js init, Tailwind
3Supabase Auth + professor/student roles
4FastAPI init + file upload + parsing
5RAG pipeline (OpenAI embeddings + pgvector)
6Professor upload UI
7Student chat UI
8Professor analytics dashboard
9Connect frontend ↔ backend
10Landing / onboarding page
11Deploy frontend + backend
12Polish + demo prep

Pitch Angles

  • Not replacing professors — augmenting their time, not their job
  • Scoped AI — constrained strictly to course content, addresses AI cheating concerns
  • Hints not answers — guides students toward understanding, preserves learning
  • Professor insights — analytics on what topics students struggle with most
  • Real problem — professors waste hours on repetitive questions, students wait days for help

OpenHours — Built for the Intellectual Pursuit track. Projects must be entirely new.

About

OpenHours lets professors upload their course materials (PDFs, slides, lecture videos) and gives students an AI assistant that answers questions strictly based on that content. The AI hints and guides rather than just giving answers.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Contributors

Languages