Skip to content

baggan1/opound-ai-chatbot

Repository files navigation

Dental AI Chatbot

AI-powered patient engagement chatbot for dental practices. Built as a portfolio project for AI consulting.

Features

  • RAG Chatbot — Answers patient questions via Pinecone + OpenAI GPT-4o
  • Appointment Booking — Collects patient info and presents a Calendly booking link
  • CRM Sync — Creates/updates contacts and logs conversation summaries to HubSpot
  • Admin Dashboard — Clerk-authenticated portal for the practice owner

Tech Stack

Layer Tech
Framework Next.js 14 (App Router, TypeScript strict)
Hosting Vercel
Auth Clerk (admin only)
Database Supabase (PostgreSQL)
Vector DB Pinecone (demo-dental namespace)
LLM OpenAI GPT-4o
Embeddings OpenAI text-embedding-3-small
Scheduling Calendly v2 API
CRM HubSpot API v3
Email Resend (Phase 4)
Styling Tailwind CSS

Setup

1. Clone & Install

git clone <repo-url>
cd dental-ai-chatbot
npm install

2. Supabase Setup

  1. Go to supabase.com and create a new project
  2. Open the SQL Editor and run the full SQL block below
  3. Copy your keys from Settings → API:
    • Project URLNEXT_PUBLIC_SUPABASE_URL
    • anon / public key → NEXT_PUBLIC_SUPABASE_ANON_KEY
    • service_role key → SUPABASE_SERVICE_ROLE_KEY
Click to view full SQL schema
-- conversations
create table if not exists conversations (
  id uuid primary key default gen_random_uuid(),
  created_at timestamptz default now(),
  patient_name text,
  patient_email text,
  patient_phone text,
  messages jsonb,
  intent_summary text,
  booking_url_presented boolean default false,
  hubspot_contact_id text,
  tenant_id text default 'demo-dental'
);

-- documents
create table if not exists documents (
  id uuid primary key default gen_random_uuid(),
  created_at timestamptz default now(),
  name text not null,
  type text not null check (type in ('pdf', 'website')),
  status text not null default 'processing' check (status in ('processing', 'ready', 'error')),
  chunk_count integer default 0,
  tenant_id text default 'demo-dental'
);

-- tenant_settings
create table if not exists tenant_settings (
  id uuid primary key default gen_random_uuid(),
  tenant_id text unique default 'demo-dental',
  practice_name text,
  calendly_scheduling_url text,
  calendly_event_type_uri text,
  hubspot_access_token text,
  emergency_phone text,
  welcome_message text,
  updated_at timestamptz default now()
);

-- Enable RLS
alter table conversations enable row level security;
alter table documents enable row level security;
alter table tenant_settings enable row level security;

-- Permissive policies (tighten before multi-tenant production)
create policy "Service role full access" on conversations for all using (true) with check (true);
create policy "Service role full access" on documents for all using (true) with check (true);
create policy "Service role full access" on tenant_settings for all using (true) with check (true);

-- Seed initial settings row
insert into tenant_settings (
  tenant_id, practice_name, welcome_message, emergency_phone
) values (
  'demo-dental',
  'Demo Dental Practice',
  'Hi 👋 I''m your dental assistant. How can I help you today?',
  '(555) 000-0000'
) on conflict (tenant_id) do nothing;

3. Clerk Setup

  1. Go to clerk.com and create a new application
  2. Choose Email + Password (or add social logins as desired)
  3. Copy your keys from the Clerk Dashboard:
    • Publishable KeyNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
    • Secret KeyCLERK_SECRET_KEY
  4. In Clerk Dashboard → Paths, set:
    • Sign-in URL: /admin/sign-in
    • After sign-in URL: /admin/dashboard

4. Pinecone Setup

  1. Go to pinecone.io and create an index
  2. Dimensions: 1536, Metric: cosine
  3. Copy your API key → PINECONE_API_KEY
  4. Copy your index name → PINECONE_INDEX

5. Other Integrations

  • OpenAI: Get API key from platform.openai.com
  • Calendly: Get API key and set up an event type in your Calendly account
  • HubSpot: Create a private app in HubSpot and copy the access token
  • HubSpot Portal ID (optional): Found in HubSpot → Account & Billing. Used to generate direct links to contacts in the dashboard.

6. Environment Variables

Create .env.local in the project root:

# OpenAI
OPENAI_API_KEY=

# Pinecone
PINECONE_API_KEY=
PINECONE_INDEX=

# Calendly
CALENDLY_API_KEY=
CALENDLY_EVENT_TYPE_URI=https://api.calendly.com/event_types/XXXX
CALENDLY_SCHEDULING_URL=https://calendly.com/yourname/30min

# HubSpot
HUBSPOT_ACCESS_TOKEN=
NEXT_PUBLIC_HUBSPOT_PORTAL_ID=   # Optional — enables direct contact links in dashboard

# Resend (Phase 4)
RESEND_API_KEY=

# Supabase
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=

# Clerk
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/admin/sign-in
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/admin/dashboard

Running Locally

npm run dev

Ingesting Documents

Bulk-ingest all PDFs from the /data folder:

npm run ingest -- --pdf

Ingest Single pdf

npm run ingest -- --file=crowns-bridges.pdf  

Ingest a website URL:

npm run ingest -- --url=https://yourpractice.com/services

You can also ingest documents through the admin dashboard at /admin/documents.


Seeding Initial Settings

The SQL above seeds a default tenant_settings row for demo-dental. After signing in to the admin dashboard, go to Settings to update:

  • Practice name
  • Chatbot welcome message
  • Emergency phone number
  • Calendly URLs
  • HubSpot access token

Admin Dashboard

Route Description
/admin/sign-in Clerk-authenticated login
/admin/dashboard Analytics overview
/admin/conversations All patient chat sessions
/admin/conversations/[id] Full transcript + patient details
/admin/documents Manage RAG data sources
/admin/settings Practice configuration

Build Status

Phase Status
Phase 1 — RAG chatbot (Pinecone + GPT-4o) ✅ Complete
Phase 2 — Calendly booking + HubSpot CRM ✅ Complete
Phase 3 — Admin dashboard (Clerk + Supabase) ✅ Complete
Phase 4 — Multi-tenancy + Stripe + webhooks + email 🔲 Not Started

Architecture Notes

  • Single-tenant for now (tenant_id = 'demo-dental' hardcoded). Multi-tenancy requires adding a tenantId lookup layer — no rewrites.
  • Uses openai.chat.completions.create() — never the Responses API
  • Single OpenAI client in lib/openai.ts — never instantiate a second
  • HubSpot and Resend calls are fire-and-forget — never block chat response
  • Supabase conversation saves are fire-and-forget — never break chat
  • Settings are cached for 60s in lib/settings.ts to avoid DB hits per message

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors