Quick Navigation
Jump to any section of this portfolio:
Project Overview
A standalone AI-powered customer support portal built with Ruby on Rails 8 that serves as the intelligent help system for the LuvNote dating application. The portal uses OpenAI GPT-4o-mini to understand user questions in natural language and route them to the most relevant support articles, in-app deep links, or fallback responses based on confidence scoring.
Key Capabilities:
- AI-powered question routing using OpenAI GPT-4o-mini Chat Completions API with structured JSON responses
- Confidence-based response tiers: fallback (<50%), support page links (50-80%), or deep app links (>80%)
- Session-based conversation tracking with cookie-based visitor fingerprinting
- Admin analytics dashboard showing conversation metrics, message counts, and session details
- Support page content management with slug-based routing and markdown rendering
- Fallback keyword-based matching service using weighted scoring across page fields
7 database migrations 3 service objects 4 controllers 4 models PostgreSQL database
Feature Walkthrough
AI-Powered Chat Interface
The main chat interface allows users to ask questions about the LuvNote app in natural language. Each question is processed by the OpenAI GPT-4o-mini model, which analyzes the query against the support page database and returns a structured JSON response with the best matching page, confidence score, and a helpful answer.
How It Works:
- User Submits Question: User types a question like "I can't find any events in my calendar" into the chat input
- Message Storage: ChatController stores the user message in the messages table linked to their conversation
- AI Routing: OpenaiSupportRouter sends the question and all support pages to GPT-4o-mini for analysis
- Confidence Scoring: AI returns a confidence score (0.0-1.0) indicating how well it understood the question
-
Response Tiers: Based on confidence:
- Below 50%: Returns a fallback "I didn't understand" message
- 50-80%: Links to the relevant support page for reading
- Above 80%: Deep links directly into the LuvNote app feature
- Response Display: Assistant response appears with confidence percentage, timestamp, and optional action link
Conversation Session Management
The ConversationFinder service implements cookie-based session management that persists across browser sessions. Each visitor gets a unique UUID stored in a permanent cookie, and their conversation history is maintained across visits without requiring user authentication.
Session Flow:
- First Visit: ConversationFinder generates a SecureRandom UUID and stores it in a permanent cookie (luvnote_support_visitor_id)
- Conversation Creation: A new Conversation record is created with visitor_id, started_at, user_agent, and a SHA-256 hash of the user's IP address
- Return Visits: Cookie is read to find existing conversation, and last_activity_at is updated
- Clear Conversation: Users can clear their chat history; this sets cleared_at timestamp rather than deleting messages (soft delete for analytics)
- Privacy: IP addresses are hashed with SHA-256 before storage — raw IPs are never persisted
Admin Analytics Dashboard
A protected admin dashboard provides real-time analytics on support conversations. Accessible via a client-side passcode modal, the dashboard shows aggregate metrics, recent conversations, and detailed message histories with confidence scores and link metadata.
Dashboard Features:
- Stats Grid: Four metric cards showing total conversations, total messages, conversations today, and messages today
- Recent Conversations: Up to 50 conversations sorted by last activity, each showing session ID, timestamp, message count, and preview of first user message
-
Conversation Detail View: Full message history with:
- Visitor metadata (ID, started, last activity, total messages)
- Complete chronological message list with role, timestamp, and confidence %
- Cleared messages shown with reduced opacity and "Hidden from user" tag
- Link metadata showing type (in_app/support) and URL for each response
- Cleared Badge: Conversations where the user cleared their history display a "Cleared" badge with the cleared_at timestamp
Support Page Content System
Support articles are stored as SupportPage records with slug-based routing, markdown body content, tags for AI matching, and optional in-app deep links. The AI references these pages when routing questions, and users can browse them directly.
Content System:
- SupportPage Model: Each article has slug (unique), title, summary, body_md (markdown), in_app_url, and tags fields
- Slug Routing: Articles are accessed at /support/:slug with clean URLs (e.g., /support/calendar-events)
- AI Integration: When a user asks a question, all support pages are serialized (slug, title, summary, tags) and sent to the AI for matching
- Deep Links: Pages with in_app_url values allow the AI to link users directly into the relevant LuvNote app feature
- Message Tracking: Each assistant message stores a foreign key (chosen_support_page_id) to track which page was matched
Fallback Keyword Matching Engine
SupportPageMatcher provides a fallback keyword-based matching engine that can route questions without the OpenAI API. It uses tokenization, stopword filtering, weighted field scoring, and margin-based confidence calculation to find the best matching support page.
Matching Algorithm:
- Text Normalization: Query is lowercased, stripped, and non-alphanumeric characters are removed
- Keyword Extraction: Tokens shorter than 2 characters and English stopwords (40+ words) are filtered out
-
Weighted Field Scoring: Keywords are matched against each page's fields with different weights:
- Tags: weight 6 (highest priority)
- Slug: weight 5
- Title: weight 4
- Summary: weight 2
- Body: weight 1
- Overlap Ratio: For each field, the overlap ratio (matched keywords / total keywords) is calculated and multiplied by the field weight
- Exact Phrase Bonus: +0.5 bonus if the full keyword phrase appears in the title or slug
- Confidence Calculation: Base confidence from best_score / approx_max, plus a margin bonus when the best match is significantly better than the second-best (up to +0.15 for margins >= 2.5)
Velvet Theme UI Design
The entire support portal uses a cohesive "Velvet Theme" design system built with CSS custom properties. The dark purple/magenta color scheme features gradient backgrounds, glassmorphism effects, custom scrollbars, and smooth transitions throughout.
Design System Features:
- CSS Variables: 12+ custom properties (--velvet-background, --velvet-primary, --velvet-secondary, etc.) ensure consistent theming
- Chat Bubbles: User messages (right-aligned, pink gradient) and assistant messages (left-aligned, dark gradient) with asymmetric border-radius
- Responsive Breakpoints: Three breakpoints (768px, 480px) with adjusted padding, font sizes, and grid layouts
- Admin Dashboard Cards: Stats grid with auto-fit responsive columns, conversation cards with hover glow effects
- Modal Design: Admin access modal with backdrop blur (5px), fixed positioning, and animated transitions
- Custom Scrollbar: Chat container uses themed scrollbar colors matching the velvet palette
Technical Architecture
System Architecture
LuvNote Support Portal follows the Rails MVC pattern with a service-oriented architecture. The application separates concerns into controllers, models, services, and views, with the OpenAI integration abstracted into dedicated service objects.
Web Framework
- Ruby on Rails 8.1 — Latest Rails framework with modern defaults and Turbo Rails for SPA-like navigation
- Turbo Rails — Hotwire-powered page transitions without full page reloads
- Stimulus — Lightweight JavaScript controllers for interactive behavior
- Importmap — ES module bundling without Node.js or webpack
MVC Layer
- ChatController — Handles chat index, message creation (POST /chat), and conversation clearing (POST /chat/clear)
- AdminController — Dashboard analytics, conversation detail views, and session management
- SupportController — Renders individual support pages by slug (/support/:slug)
- ApplicationController — Base controller with shared configuration
Service Layer
- OpenaiSupportRouter — Wraps OpenAI Chat Completions API for intelligent question routing with structured JSON responses
- SupportPageMatcher — Keyword-based fallback matching with weighted scoring, stopword filtering, and confidence calculation
- ConversationFinder — Cookie-based session management with UUID generation and IP hashing
Data Layer
- PostgreSQL — Primary relational database with indexed columns for fast lookups
- ActiveRecord ORM — Rails ORM with associations, validations, and migrations
- 7 Database Migrations — Version-controlled schema evolution with proper foreign keys and indexes
- 4 Models — Conversation, Message, SupportPage, AdminSession with has_many/belongs_to associations
AI Integration
- OpenAI GPT-4o-mini — Primary AI model for question understanding and routing via Chat Completions API
- Structured JSON Responses — System prompt enforces strict JSON schema (support_page_slug, confidence, answer, reason)
- Configurable Parameters — Model, temperature, and max_tokens configurable via environment variables or constants
- Error Handling — Graceful fallbacks for JSON parsing errors, API failures, and NaN confidence values
Database Schema
- conversations — visitor_id (indexed, unique), started_at, last_activity_at, user_agent, ip_hash, cleared_at
- messages — conversation_id (FK), role, content, model, confidence, chosen_support_page_id (FK), link_type, link_url, latency_ms, prompt_tokens, completion_tokens
- support_pages — slug (unique), title, summary, body_md, in_app_url, tags
- event_logs — conversation_id (FK), event_name, level, data_json (JSONB)
- admin_sessions — session_token, expires_at
Service Layer Deep Dive
OpenaiSupportRouter
The primary AI service that routes user questions to the most relevant support page. Sends the user's question along with all support pages to GPT-4o-mini and parses the structured JSON response.
Implementation Details:
- Initialization: Creates OpenAI::Client with API key sourced from either Rails constants or ENV variables
- Page Serialization: All SupportPage records are mapped to {slug, title, summary, tags} hashes for the AI context
- System Prompt: Defines the AI as a LuvNote support assistant, enforces strict JSON-only output, and provides the exact response schema
- API Call: Uses chat() method with configurable model, temperature, and max_tokens parameters
- Response Parsing: Extracts support_page_slug, confidence, answer, and reason from the JSON response
- Page Lookup: Finds the matched SupportPage by slug, first checking the in-memory pages array, then falling back to a database query
- Confidence Clamping: Values are clamped to 0.0-1.0 range and NaN values are converted to 0.0
- Error Handling: JSON::ParserError and StandardError are caught and return a zero-confidence Result with the error class name
SupportPageMatcher
A self-contained keyword matching engine that provides intelligent support page routing without any external API dependency. Built with NLP fundamentals: tokenization, stopword removal, weighted field matching, and margin-based confidence scoring.
Algorithm Steps:
- Normalize & Tokenize: Downcase, strip, remove non-alphanumeric characters, split on whitespace, reject tokens < 2 chars, deduplicate
- Stopword Filtering: Remove 40+ English stopwords (the, a, an, help, please, etc.) from extracted keywords
- Score Each Page: Calculate overlap_ratio (hits / total keywords) for each field, multiply by field weight, sum all scores
- Exact Phrase Bonus: Add +0.5 if the joined keyword phrase appears in the page title or slug
- Rank Pages: Sort all pages by score descending; extract best and second-best scores
- Confidence Calculation: base = best_score / approx_max, plus margin_bonus (0.05 to 0.15 based on gap between best and second-best)
ConversationFinder
Manages visitor sessions using Rails permanent cookies and SecureRandom UUIDs. Provides find-or-create semantics with activity tracking and privacy-compliant IP hashing.
Session Lifecycle:
- Cookie Check: Reads luvnote_support_visitor_id from cookies
- New Visitor: If no cookie exists, generates SecureRandom.uuid and stores in cookies.permanent
- Existing Visitor: Finds Conversation by visitor_id and calls touch! to update last_activity_at
- New Conversation: Creates Conversation with visitor_id, started_at, last_activity_at, user_agent, and ip_hash
- IP Privacy: ip_hash method uses Digest::SHA256.hexdigest to one-way hash the IP address before storage
Skills Demonstrated
Ruby on Rails
- Rails 8.1 with modern defaults
- MVC architecture pattern
- ActiveRecord ORM & associations
- Service object pattern
- Database migrations & schema management
- Turbo Rails & Stimulus
AI & NLP
- OpenAI GPT-4o-mini integration
- Chat Completions API
- Structured JSON prompt engineering
- Confidence scoring & thresholds
- Keyword tokenization & stopwords
- Weighted scoring algorithms
Database Design
- PostgreSQL relational database
- Foreign key constraints
- Indexed columns for performance
- JSONB columns for flexible data
- Soft delete patterns (cleared_at)
Web Development
- ERB templating
- CSS custom properties design system
- Responsive design (3 breakpoints)
- Cookie-based session management
- RESTful routing
Security & Privacy
- SHA-256 IP hashing for privacy
- CSRF protection (Rails default)
- Input sanitization (html_escape)
- Secure cookie management
- Environment variable configuration
Software Design
- Service object pattern
- Struct-based result objects
- Graceful error handling
- Configurable via ENV or constants
- Clean separation of concerns
Key Achievements
AI Integration
OpenAI GPT-4o-mini with structured JSON responses and confidence scoring
Rails 8.1
Built on the latest Ruby on Rails framework with modern conventions
Dual Routing
AI-powered primary router with keyword-based fallback engine
Analytics Dashboard
Real-time admin analytics with conversation tracking and metrics
Privacy-First
SHA-256 IP hashing and cookie-based sessions without authentication
Velvet Theme
813 lines of custom CSS with design system and responsive layouts
Service Architecture
Clean service object pattern with Struct results and error handling
Production Ready
PostgreSQL database with migrations, indexes, and foreign keys