diff --git a/week2/community-contributions/hopeogbons/README.md b/week2/community-contributions/hopeogbons/README.md
new file mode 100644
index 0000000..953a7e5
--- /dev/null
+++ b/week2/community-contributions/hopeogbons/README.md
@@ -0,0 +1,355 @@
+# 🏥 RoboCare AI Assistant
+
+> Born from a real problem at MyWoosah Inc—now solving caregiver matching through AI.
+
+## 📋 The Story Behind This Project
+
+While working on a caregiver matching platform for **MyWoosah Inc** in the US, I faced a real challenge: how do you efficiently match families with the right caregivers when everyone has different needs?
+
+Families would ask things like:
+
+- _"I need someone for my mom on Monday mornings who speaks Spanish"_
+- _"Can you find elder care in Boston under $30/hour with CPR certification?"_
+
+Writing individual SQL queries for every combination of filters was exhausting and error-prone. I knew there had to be a better way.
+
+That's when I discovered the **Andela LLM Engineering program**. I saw an opportunity to transform this problem into a solution using AI. Instead of rigid queries, what if families could just... talk? And the AI would understand, search, and recommend?
+
+This project is my answer. It's not just an exercise—it's solving a real problem I encountered in the field.
+
+## What It Does
+
+RoboCare helps families find caregivers through natural conversation:
+
+- 🔍 Searches the database intelligently
+- 🎯 Finds the best matches
+- 💬 Explains pros/cons in plain English
+- 🔊 Speaks the results back to you
+
+## ✨ Features
+
+### 🤖 AI-Powered Matching
+
+- Natural language conversation interface
+- Intelligent requirement gathering
+- Multi-criteria search optimization
+- Personalized recommendations with pros/cons analysis
+
+### 🔍 Advanced Search Capabilities
+
+- **Location-based filtering**: City, state, and country
+- **Service type matching**: Elder care, child care, companionship, dementia care, hospice support, and more
+- **Availability scheduling**: Day and time-based matching
+- **Budget optimization**: Maximum hourly rate filtering
+- **Language preferences**: Multi-language support
+- **Certification requirements**: CPR, CNA, BLS, and specialized certifications
+- **Experience filtering**: Minimum years of experience
+
+### 🎙️ Multi-Modal Interface
+
+- Text-based chat interface
+- Voice response generation (Text-to-Speech)
+- Multiple voice options (coral, alloy, echo, fable, onyx, nova, shimmer)
+- Clean, modern UI built with Gradio
+
+### 🛡️ Defensive Architecture
+
+- Comprehensive error handling
+- Token overflow protection
+- Tool call validation
+- Graceful degradation
+
+## 🚀 Getting Started
+
+### Prerequisites
+
+- Python 3.8+
+- OpenAI API key
+- Virtual environment (recommended)
+
+### Installation
+
+1. **Clone the repository**
+
+ ```bash
+ cd week2
+ ```
+
+2. **Create and activate virtual environment**
+
+ ```bash
+ python -m venv .venv
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
+ ```
+
+3. **Install dependencies**
+
+ ```bash
+ pip install -r requirements.txt
+ ```
+
+4. **Set up environment variables**
+
+ Create a `.env` file in the project root:
+
+ ```env
+ OPENAI_API_KEY=your_openai_api_key_here
+ ```
+
+5. **Run the application**
+
+ ```bash
+ jupyter notebook "week2 EXERCISE.ipynb"
+ ```
+
+ Or run all cells sequentially in your Jupyter environment.
+
+## 📊 Database Schema
+
+### Tables
+
+#### `caregivers`
+
+Primary caregiver information including:
+
+- Personal details (name, gender)
+- Experience level
+- Hourly rate and currency
+- Location (city, state, country, coordinates)
+- Live-in availability
+
+#### `caregiver_services`
+
+Care types offered by each caregiver:
+
+- Elder care
+- Child care
+- Companionship
+- Post-op support
+- Special needs
+- Respite care
+- Dementia care
+- Hospice support
+
+#### `availability`
+
+Time slots when caregivers are available:
+
+- Day of week (Mon-Sun)
+- Start and end times (24-hour format)
+
+#### `languages`
+
+Languages spoken by caregivers
+
+#### `certifications`
+
+Professional certifications (CPR, CNA, BLS, etc.)
+
+#### `traits`
+
+Personality and professional traits
+
+## 🔧 Architecture
+
+### Tool Registry Pattern
+
+```python
+TOOL_REGISTRY = {
+ "search_caregivers": search_caregivers,
+ "get_caregiver_profile": get_caregiver_profile,
+ # ... more tools
+}
+```
+
+All database functions are registered and can be called by the AI dynamically.
+
+### Search Functions
+
+#### `search_caregivers()`
+
+Multi-filter search with parameters:
+
+- `city`, `state_province`, `country` - Location filters
+- `care_type` - Type of care needed
+- `min_experience` - Minimum years of experience
+- `max_hourly_rate` - Budget constraint
+- `live_in` - Live-in caregiver requirement
+- `language` - Language preference
+- `certification` - Required certification
+- `day` - Day of week availability
+- `time_between` - Time window availability
+- `limit`, `offset` - Pagination
+
+#### `get_caregiver_profile(caregiver_id)`
+
+Returns complete profile including:
+
+- Basic information
+- Services offered
+- Languages spoken
+- Certifications
+- Personality traits
+- Availability schedule
+
+## 🎨 UI Components
+
+### Main Interface
+
+- **Chat History**: Message-based conversation display
+- **Voice Response**: Auto-playing audio output
+- **Settings Sidebar**:
+ - AI Model selector
+ - Voice selection
+ - Audio toggle
+ - Clear conversation button
+
+### User Experience
+
+- Professional gradient header
+- Collapsible instructions
+- Helpful placeholder text
+- Custom CSS styling
+- Responsive layout
+
+## 📝 Usage Examples
+
+### Example 1: Basic Search
+
+```python
+results = search_caregivers(
+ city="New York",
+ care_type="elder care",
+ max_hourly_rate=30.0,
+ limit=5
+)
+```
+
+### Example 2: Language Filter
+
+```python
+results = search_caregivers(
+ care_type="child care",
+ language="Spanish",
+ limit=3
+)
+```
+
+### Example 3: Availability Search
+
+```python
+results = search_caregivers(
+ day="Mon",
+ time_between=("09:00", "17:00"),
+ city="Boston"
+)
+```
+
+### Example 4: Get Full Profile
+
+```python
+profile = get_caregiver_profile(caregiver_id=1)
+print(profile['services'])
+print(profile['availability'])
+```
+
+## 🔐 Security & Best Practices
+
+### Current Implementation
+
+- ✅ Environment variable management for API keys
+- ✅ SQL injection prevention (parameterized queries)
+- ✅ Error handling and graceful degradation
+- ✅ Input validation through tool schemas
+
+### Important Disclaimers
+
+⚠️ **This is a demonstration application**
+
+- Credentials and background checks are NOT verified
+- Families should independently verify all caregiver information
+- Not intended for production use without additional security measures
+
+## 🛠️ Tech Stack
+
+- **AI/ML**: OpenAI GPT-4o-mini, Text-to-Speech API
+- **Database**: SQLite with normalized schema
+- **UI Framework**: Gradio
+- **Language**: Python 3.8+
+- **Key Libraries**:
+ - `openai` - OpenAI API client
+ - `gradio` - Web interface
+ - `python-dotenv` - Environment management
+ - `sqlite3` - Database operations
+
+## 📈 What's Next
+
+### Immediate Plans
+
+- [ ] Add speech input (families could call and talk)
+- [ ] Connect to actual MyWoosah database
+- [ ] Background check API integration
+- [ ] Deploy for real users
+
+### Future Enhancements
+
+- [ ] Streaming responses for real-time interaction
+- [ ] Dynamic model switching
+- [ ] User authentication and profiles
+- [ ] Review and rating system
+- [ ] Payment integration
+- [ ] Calendar integration for scheduling
+
+## 💡 Key Learnings
+
+Through building this project, I learned:
+
+1. **Prompt engineering is critical** - Small keyword mismatches = zero results. Mapping "Monday" → "Mon" matters.
+2. **Function calling is powerful** - Eliminated the need for custom queries. The AI figures it out.
+3. **Defensive programming saves headaches** - Things break. This code expects it and handles it elegantly.
+4. **AI makes databases accessible** - Good database design + AI = natural language interface
+
+## 🌍 The Bigger Picture
+
+This isn't just about caregiving. The same pattern works for:
+
+- Healthcare appointment booking
+- Legal service matching
+- Tutoring and education platforms
+- Real estate agent matching
+- Any matching problem where natural language beats forms
+
+**AI doesn't replace good database design—it makes it accessible to everyone.**
+
+---
+
+## 🤝 Contributing
+
+This project was created as part of the **Andela LLM Engineering Week 2 Exercise**.
+
+Feedback and contributions are welcome! Feel free to:
+
+1. Fork the repository
+2. Create a feature branch
+3. Make your changes
+4. Run all cells to test
+5. Submit a pull request
+
+## 🙏 Acknowledgments
+
+- **MyWoosah Inc** - For the real-world problem that inspired this solution
+- **Andela LLM Engineering Program** - Educational framework and guidance
+- **OpenAI** - GPT-4o and TTS API
+- **Gradio** - Making beautiful UIs accessible
+
+---
+
+
+
+**For MyWoosah Inc and beyond:** This is proof that AI can transform how we connect people with the care they need.
+
+_Built with ❤️ during Week 2 of the Andela LLM Engineering Program_
+
+**RoboOffice Ltd**
+
+
diff --git a/week2/community-contributions/hopeogbons/care_app.db b/week2/community-contributions/hopeogbons/care_app.db
new file mode 100644
index 0000000..93f8fdb
Binary files /dev/null and b/week2/community-contributions/hopeogbons/care_app.db differ
diff --git a/week2/community-contributions/hopeogbons/week2 EXERCISE.ipynb b/week2/community-contributions/hopeogbons/week2 EXERCISE.ipynb
new file mode 100644
index 0000000..6915f24
--- /dev/null
+++ b/week2/community-contributions/hopeogbons/week2 EXERCISE.ipynb
@@ -0,0 +1,1525 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "d006b2ea-9dfe-49c7-88a9-a5a0775185fd",
+ "metadata": {},
+ "source": [
+ "# 🏥 RoboCare AI Assistant\n",
+ "\n",
+ "## Why I Built This\n",
+ "\n",
+ "While working on a caregiver matching platform for **MyWoosah Inc** in the US, I faced a real challenge: how do you efficiently match families with the right caregivers when everyone has different needs?\n",
+ "\n",
+ "Families would ask things like:\n",
+ "- *\"I need someone for my mom on Monday mornings who speaks Spanish\"*\n",
+ "- *\"Can you find elder care in Boston under $30/hour with CPR certification?\"*\n",
+ "\n",
+ "Writing individual SQL queries for every combination of filters was exhausting and error-prone. I knew there had to be a better way.\n",
+ "\n",
+ "That's when I discovered the **Andela LLM Engineering program**. I saw an opportunity to transform this problem into a solution using AI. Instead of rigid queries, what if families could just... talk? And the AI would understand, search, and recommend?\n",
+ "\n",
+ "This project is my answer. It's not just an exercise—it's solving a real problem I encountered in the field.\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## What This Does\n",
+ "\n",
+ "RoboCare helps families find caregivers through natural conversation. You tell it what you need, and it:\n",
+ "- 🔍 Searches the database intelligently\n",
+ "- 🎯 Finds the best matches\n",
+ "- 💬 Explains pros/cons in plain English \n",
+ "- 🔊 Speaks the results back to you\n",
+ "\n",
+ "**Tech:** OpenAI GPT-4o + Voice • Gradio UI • SQLite Database • Function Calling\n",
+ "\n",
+ "---\n",
+ "\n",
+ "**Note:** This is a demonstration. Always verify credentials independently."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4381c40c",
+ "metadata": {},
+ "source": [
+ "## Step 1: Libraries\n",
+ "\n",
+ "The essentials: OpenAI for the AI brain, Gradio for the interface, SQLite for data storage.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 63,
+ "id": "185c6841",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# imports\n",
+ "\n",
+ "import os\n",
+ "from dotenv import load_dotenv\n",
+ "from openai import OpenAI\n",
+ "import gradio as gr\n",
+ "import sqlite3\n",
+ "import sqlite3\n",
+ "from textwrap import dedent\n",
+ "from contextlib import contextmanager\n",
+ "from typing import Optional, List, Dict, Any, Tuple"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2a366c15",
+ "metadata": {},
+ "source": [
+ "## Step 2: Setup\n",
+ "\n",
+ "Loading API keys securely (never hardcode them!), setting up the OpenAI client, and pointing to our database.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 64,
+ "id": "0e731b96",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "OpenAI API Key exists and begins sk-proj-\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Initialization\n",
+ "\n",
+ "load_dotenv(override=True)\n",
+ "\n",
+ "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
+ "if openai_api_key:\n",
+ " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
+ "else:\n",
+ " print(\"OpenAI API Key not set\")\n",
+ " \n",
+ "MODEL = \"gpt-4o-mini\"\n",
+ "openai = OpenAI()\n",
+ "\n",
+ "DB_PATH = \"care_app.db\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "686fa27a",
+ "metadata": {},
+ "source": [
+ "## Step 3: The Database\n",
+ "\n",
+ "20 sample caregivers across major US cities with:\n",
+ "- Services they offer (elder care, child care, etc.)\n",
+ "- Languages, certifications, availability\n",
+ "- Personality traits\n",
+ "- Realistic pricing and schedules\n",
+ "\n",
+ "This mirrors the kind of data MyWoosah Inc would manage—except here, AI does the matching work.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 65,
+ "id": "965d273d",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Seeded: care_app.db\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Table creation and seeding\n",
+ "\n",
+ "SQL = '''\n",
+ "\n",
+ " CREATE TABLE IF NOT EXISTS caregivers (\n",
+ " id INTEGER PRIMARY KEY,\n",
+ " name TEXT NOT NULL,\n",
+ " gender TEXT,\n",
+ " years_experience INTEGER,\n",
+ " live_in INTEGER, -- 0/1\n",
+ " hourly_rate REAL,\n",
+ " currency TEXT,\n",
+ " city TEXT,\n",
+ " state_province TEXT,\n",
+ " country TEXT,\n",
+ " postal_code TEXT,\n",
+ " lat REAL,\n",
+ " lon REAL\n",
+ " );\n",
+ "\n",
+ " CREATE TABLE IF NOT EXISTS caregiver_services (\n",
+ " caregiver_id INTEGER,\n",
+ " care_type TEXT,\n",
+ " FOREIGN KEY (caregiver_id) REFERENCES caregivers(id)\n",
+ " );\n",
+ "\n",
+ " CREATE TABLE IF NOT EXISTS availability (\n",
+ " caregiver_id INTEGER,\n",
+ " day TEXT, -- e.g., 'Mon'\n",
+ " time_start TEXT, -- 'HH:MM'\n",
+ " time_end TEXT, -- 'HH:MM'\n",
+ " FOREIGN KEY (caregiver_id) REFERENCES caregivers(id)\n",
+ " );\n",
+ "\n",
+ " CREATE TABLE IF NOT EXISTS languages (\n",
+ " caregiver_id INTEGER,\n",
+ " language TEXT,\n",
+ " FOREIGN KEY (caregiver_id) REFERENCES caregivers(id)\n",
+ " );\n",
+ "\n",
+ " CREATE TABLE IF NOT EXISTS certifications (\n",
+ " caregiver_id INTEGER,\n",
+ " cert TEXT,\n",
+ " FOREIGN KEY (caregiver_id) REFERENCES caregivers(id)\n",
+ " );\n",
+ "\n",
+ " CREATE TABLE IF NOT EXISTS traits (\n",
+ " caregiver_id INTEGER,\n",
+ " trait TEXT,\n",
+ " FOREIGN KEY (caregiver_id) REFERENCES caregivers(id)\n",
+ " );\n",
+ "\n",
+ " ----------------------------------------------------------\n",
+ "\n",
+ " -- Clear old data (optional)\n",
+ "\n",
+ " DELETE FROM traits;\n",
+ " DELETE FROM certifications;\n",
+ " DELETE FROM languages;\n",
+ " DELETE FROM availability;\n",
+ " DELETE FROM caregiver_services;\n",
+ " DELETE FROM caregivers;\n",
+ "\n",
+ " -- Seed caregivers (20 examples, all USA)\n",
+ "\n",
+ " INSERT INTO caregivers\n",
+ " (id, name, gender, years_experience, live_in, hourly_rate, currency, city, state_province, country, postal_code, lat, lon)\n",
+ " VALUES\n",
+ " (1, 'Grace Williams', 'female', 6, 0, 28, 'USD', 'New York', 'NY', 'USA', '10001', 40.7128, -74.0060),\n",
+ " (2, 'Miguel Alvarez', 'male', 9, 1, 30, 'USD', 'Los Angeles', 'CA', 'USA', '90012', 34.0522, -118.2437),\n",
+ " (3, 'Ava Johnson', 'female', 4, 0, 24, 'USD', 'Chicago', 'IL', 'USA', '60601', 41.8781, -87.6298),\n",
+ " (4, 'Noah Robinson', 'male', 12, 0, 27, 'USD', 'Houston', 'TX', 'USA', '77002', 29.7604, -95.3698),\n",
+ " (5, 'Sophia Martinez', 'female', 8, 0, 29, 'USD', 'Phoenix', 'AZ', 'USA', '85004', 33.4484, -112.0740),\n",
+ " (6, 'Daniel Carter', 'male', 10, 1, 31, 'USD', 'Philadelphia', 'PA', 'USA', '19103', 39.9526, -75.1652),\n",
+ " (7, 'Emily Nguyen', 'female', 7, 0, 26, 'USD', 'San Antonio', 'TX', 'USA', '78205', 29.4241, -98.4936),\n",
+ " (8, 'Olivia Kim', 'female', 5, 0, 27, 'USD', 'San Diego', 'CA', 'USA', '92101', 32.7157, -117.1611),\n",
+ " (9, 'James Thompson', 'male', 15, 1, 34, 'USD', 'Dallas', 'TX', 'USA', '75201', 32.7767, -96.7970),\n",
+ " (10, 'Isabella Garcia', 'female', 3, 0, 22, 'USD', 'San Jose', 'CA', 'USA', '95113', 37.3382, -121.8863),\n",
+ " (11, 'Ethan Patel', 'male', 11, 1, 33, 'USD', 'Austin', 'TX', 'USA', '78701', 30.2672, -97.7431),\n",
+ " (12, 'Harper Brooks', 'female', 2, 0, 20, 'USD', 'Jacksonville', 'FL', 'USA', '32202', 30.3322, -81.6557),\n",
+ " (13, 'Logan White', 'male', 6, 0, 25, 'USD', 'Fort Worth', 'TX', 'USA', '76102', 32.7555, -97.3308),\n",
+ " (14, 'Amelia Davis', 'female', 9, 0, 28, 'USD', 'Columbus', 'OH', 'USA', '43215', 39.9612, -82.9988),\n",
+ " (15, 'Charlotte Reed', 'female', 14, 1, 32, 'USD', 'Charlotte', 'NC', 'USA', '28202', 35.2271, -80.8431),\n",
+ " (16, 'Jackson Lee', 'male', 5, 0, 26, 'USD', 'San Francisco', 'CA', 'USA', '94102', 37.7749, -122.4194),\n",
+ " (17, 'Avery Chen', 'female', 7, 0, 27, 'USD', 'Seattle', 'WA', 'USA', '98101', 47.6062, -122.3321),\n",
+ " (18, 'William Turner', 'male', 13, 1, 35, 'USD', 'Denver', 'CO', 'USA', '80202', 39.7392, -104.9903),\n",
+ " (19, 'Natalie O''Brien', 'female', 16, 0, 36, 'USD', 'Boston', 'MA', 'USA', '02108', 42.3601, -71.0589),\n",
+ " (20, 'Maya Robinson', 'female', 3, 0, 23, 'USD', 'Atlanta', 'GA', 'USA', '30303', 33.7488, -84.3880);\n",
+ "\n",
+ " -- Seed caregiver services\n",
+ "\n",
+ " INSERT INTO caregiver_services (caregiver_id, care_type) VALUES\n",
+ " (1, 'elder care'), (1, 'companionship'),\n",
+ " (2, 'post-op support'), (2, 'elder care'),\n",
+ " (3, 'child care'), (3, 'special needs'),\n",
+ " (4, 'respite care'), (4, 'elder care'),\n",
+ " (5, 'dementia care'), (5, 'companionship'),\n",
+ " (6, 'elder care'), (6, 'hospice support'),\n",
+ " (7, 'child care'), (7, 'respite care'),\n",
+ " (8, 'post-op support'), (8, 'companionship'),\n",
+ " (9, 'special needs'), (9, 'elder care'),\n",
+ " (10, 'child care'), (10, 'companionship'),\n",
+ " (11, 'dementia care'), (11, 'post-op support'),\n",
+ " (12, 'child care'), (12, 'special needs'),\n",
+ " (13, 'respite care'), (13, 'companionship'),\n",
+ " (14, 'elder care'), (14, 'post-op support'),\n",
+ " (15, 'hospice support'), (15, 'dementia care'),\n",
+ " (16, 'elder care'), (16, 'respite care'),\n",
+ " (17, 'special needs'), (17, 'companionship'),\n",
+ " (18, 'post-op support'), (18, 'elder care'),\n",
+ " (19, 'dementia care'), (19, 'hospice support'),\n",
+ " (20, 'child care'), (20, 'companionship');\n",
+ "\n",
+ " -- Seed availability (Mon-Sun samples)\n",
+ "\n",
+ " INSERT INTO availability (caregiver_id, day, time_start, time_end) VALUES\n",
+ " -- 1 Grace (NY): evenings + Sun\n",
+ " (1, 'Mon', '17:30', '22:00'),\n",
+ " (1, 'Thu', '17:30', '22:00'),\n",
+ " (1, 'Sun', '10:00', '16:00'),\n",
+ " -- 2 Miguel (LA): live-in, long blocks\n",
+ " (2, 'Tue', '08:00', '20:00'),\n",
+ " (2, 'Thu', '08:00', '20:00'),\n",
+ " (2, 'Sat', '09:00', '18:00'),\n",
+ " -- 3 Ava (CHI): weekdays 09-17\n",
+ " (3, 'Mon', '09:00', '17:00'),\n",
+ " (3, 'Wed', '09:00', '17:00'),\n",
+ " (3, 'Fri', '09:00', '17:00'),\n",
+ " -- 4 Noah (HOU): Tue-Fri 08-16\n",
+ " (4, 'Tue', '08:00', '16:00'),\n",
+ " (4, 'Wed', '08:00', '16:00'),\n",
+ " (4, 'Thu', '08:00', '16:00'),\n",
+ " -- 5 Sophia (PHX): Thu-Sun 10-18\n",
+ " (5, 'Thu', '10:00', '18:00'),\n",
+ " (5, 'Fri', '10:00', '18:00'),\n",
+ " (5, 'Sat', '10:00', '18:00'),\n",
+ " -- 6 Daniel (PHL): Mon-Thu 07-15\n",
+ " (6, 'Mon', '07:00', '15:00'),\n",
+ " (6, 'Tue', '07:00', '15:00'),\n",
+ " (6, 'Thu', '07:00', '15:00'),\n",
+ " -- 7 Emily (SAT): weekends\n",
+ " (7, 'Sat', '08:00', '17:00'),\n",
+ " (7, 'Sun', '09:00', '17:00'),\n",
+ " (7, 'Fri', '17:00', '21:00'),\n",
+ " -- 8 Olivia (SD): Mon, Wed evenings\n",
+ " (8, 'Mon', '16:00', '21:00'),\n",
+ " (8, 'Wed', '16:00', '21:00'),\n",
+ " (8, 'Sat', '10:00', '14:00'),\n",
+ " -- 9 James (DAL): live-in wide\n",
+ " (9, 'Mon', '07:00', '19:00'),\n",
+ " (9, 'Wed', '07:00', '19:00'),\n",
+ " (9, 'Sun', '09:00', '17:00'),\n",
+ " -- 10 Isabella (SJ): Tue-Thu 12-20\n",
+ " (10, 'Tue', '12:00', '20:00'),\n",
+ " (10, 'Wed', '12:00', '20:00'),\n",
+ " (10, 'Thu', '12:00', '20:00'),\n",
+ " -- 11 Ethan (ATX): nights\n",
+ " (11, 'Mon', '18:00', '23:00'),\n",
+ " (11, 'Tue', '18:00', '23:00'),\n",
+ " (11, 'Fri', '18:00', '23:00'),\n",
+ " -- 12 Harper (JAX): school hours\n",
+ " (12, 'Mon', '09:00', '14:00'),\n",
+ " (12, 'Wed', '09:00', '14:00'),\n",
+ " (12, 'Fri', '09:00', '14:00'),\n",
+ " -- 13 Logan (FTW): Thu-Sat\n",
+ " (13, 'Thu', '10:00', '18:00'),\n",
+ " (13, 'Fri', '10:00', '18:00'),\n",
+ " (13, 'Sat', '10:00', '18:00'),\n",
+ " -- 14 Amelia (CMH): Mon-Fri 08-16\n",
+ " (14, 'Mon', '08:00', '16:00'),\n",
+ " (14, 'Tue', '08:00', '16:00'),\n",
+ " (14, 'Thu', '08:00', '16:00'),\n",
+ " -- 15 Charlotte (CLT): live-in style\n",
+ " (15, 'Tue', '07:00', '19:00'),\n",
+ " (15, 'Thu', '07:00', '19:00'),\n",
+ " (15, 'Sat', '08:00', '16:00'),\n",
+ " -- 16 Jackson (SF): split shifts\n",
+ " (16, 'Mon', '07:00', '11:00'),\n",
+ " (16, 'Mon', '17:00', '21:00'),\n",
+ " (16, 'Sat', '12:00', '18:00'),\n",
+ " -- 17 Avery (SEA): Tue/Thu + Sun\n",
+ " (17, 'Tue', '10:00', '18:00'),\n",
+ " (17, 'Thu', '10:00', '18:00'),\n",
+ " (17, 'Sun', '11:00', '17:00'),\n",
+ " -- 18 William (DEN): Mon-Wed 06-14\n",
+ " (18, 'Mon', '06:00', '14:00'),\n",
+ " (18, 'Tue', '06:00', '14:00'),\n",
+ " (18, 'Wed', '06:00', '14:00'),\n",
+ " -- 19 Natalie (BOS): Tue-Fri 09-17\n",
+ " (19, 'Tue', '09:00', '17:00'),\n",
+ " (19, 'Wed', '09:00', '17:00'),\n",
+ " (19, 'Fri', '09:00', '17:00'),\n",
+ " -- 20 Maya (ATL): after-school + Sat\n",
+ " (20, 'Mon', '15:00', '20:00'),\n",
+ " (20, 'Wed', '15:00', '20:00'),\n",
+ " (20, 'Sat', '09:00', '15:00');\n",
+ "\n",
+ " -- Seed languages\n",
+ "\n",
+ " INSERT INTO languages (caregiver_id, language) VALUES\n",
+ " (1, 'English'), (1, 'Spanish'),\n",
+ " (2, 'English'), (2, 'Spanish'),\n",
+ " (3, 'English'),\n",
+ " (4, 'English'),\n",
+ " (5, 'English'), (5, 'Spanish'),\n",
+ " (6, 'English'),\n",
+ " (7, 'English'), (7, 'Vietnamese'),\n",
+ " (8, 'English'), (8, 'Korean'),\n",
+ " (9, 'English'),\n",
+ " (10,'English'), (10,'Spanish'),\n",
+ " (11,'English'), (11,'Hindi'),\n",
+ " (12,'English'),\n",
+ " (13,'English'),\n",
+ " (14,'English'), (14,'French'),\n",
+ " (15,'English'),\n",
+ " (16,'English'), (16,'Tagalog'),\n",
+ " (17,'English'), (17,'Mandarin'),\n",
+ " (18,'English'),\n",
+ " (19,'English'), (19,'Portuguese'),\n",
+ " (20,'English'), (20,'ASL');\n",
+ "\n",
+ " -- Seed certifications\n",
+ "\n",
+ " INSERT INTO certifications (caregiver_id, cert) VALUES\n",
+ " (1, 'CPR'), (1, 'First Aid'),\n",
+ " (2, 'CPR'), (2, 'BLS'),\n",
+ " (3, 'CPR'),\n",
+ " (4, 'First Aid'), (4, 'CNA'),\n",
+ " (5, 'CPR'), (5, 'Dementia Care'),\n",
+ " (6, 'HHA'), (6, 'CPR'),\n",
+ " (7, 'First Aid'),\n",
+ " (8, 'CPR'), (8, 'AED'),\n",
+ " (9, 'CNA'), (9, 'BLS'),\n",
+ " (10,'First Aid'),\n",
+ " (11,'CPR'), (11,'Medication Technician'),\n",
+ " (12,'CPR'),\n",
+ " (13,'First Aid'),\n",
+ " (14,'CPR'), (14,'CNA'),\n",
+ " (15,'Hospice Training'), (15,'CPR'),\n",
+ " (16,'First Aid'),\n",
+ " (17,'CPR'), (17,'Special Needs Training'),\n",
+ " (18,'BLS'), (18,'CPR'),\n",
+ " (19,'Dementia Care'), (19,'First Aid'),\n",
+ " (20,'CPR'), (20,'Childcare Safety');\n",
+ "\n",
+ " -- Seed traits\n",
+ "\n",
+ " INSERT INTO traits (caregiver_id, trait) VALUES\n",
+ " (1, 'empathetic'), (1, 'detail-oriented'),\n",
+ " (2, 'patient'), (2, 'communicative'),\n",
+ " (3, 'cheerful'), (3, 'reliable'),\n",
+ " (4, 'organized'), (4, 'professional'),\n",
+ " (5, 'compassionate'), (5, 'trustworthy'),\n",
+ " (6, 'calm under pressure'), (6, 'punctual'),\n",
+ " (7, 'adaptable'), (7, 'energetic'),\n",
+ " (8, 'friendly'), (8, 'respectful'),\n",
+ " (9, 'thorough'), (9, 'dependable'),\n",
+ " (10,'gentle'), (10,'attentive'),\n",
+ " (11,'proactive'), (11,'communicative'),\n",
+ " (12,'patient'), (12,'kind'),\n",
+ " (13,'flexible'), (13,'tidy'),\n",
+ " (14,'reliable'), (14,'punctual'),\n",
+ " (15,'compassionate'), (15,'detail-oriented'),\n",
+ " (16,'discreet'), (16,'organized'),\n",
+ " (17,'empathetic'), (17,'calm under pressure'),\n",
+ " (18,'professional'), (18,'thorough'),\n",
+ " (19,'trustworthy'), (19,'proactive'),\n",
+ " (20,'cheerful'), (20,'attentive');\n",
+ "\n",
+ "'''\n",
+ "\n",
+ "# Insert the data into the database\n",
+ "\n",
+ "sql = dedent(SQL)\n",
+ "con = sqlite3.connect(DB_PATH)\n",
+ "con.executescript(sql)\n",
+ "con.commit()\n",
+ "con.close()\n",
+ "print(\"Seeded:\", DB_PATH)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3c0baa64",
+ "metadata": {},
+ "source": [
+ "## Step 4: Teaching the AI to Search\n",
+ "\n",
+ "Instead of the AI just talking, we teach it to actually *search* the database.\n",
+ "\n",
+ "When someone says *\"I need elder care in Boston for Mondays\"*, the AI translates that into:\n",
+ "```python\n",
+ "search_caregivers(city=\"Boston\", care_type=\"elder care\", day=\"Mon\")\n",
+ "```\n",
+ "\n",
+ "This schema defines all the filters the AI can use: location, services, budget, language, availability, and more.\n",
+ "\n",
+ "**This was the breakthrough.** No more writing custom queries—the AI figures it out.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 66,
+ "id": "f2af7c67",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[{'type': 'function',\n",
+ " 'function': {'name': 'search_caregivers',\n",
+ " 'description': 'Flexible multi-filter caregiver search. Any filter can be omitted. Supports location, service type, experience, pricing, live-in, language, certifications, day/time availability, and pagination.',\n",
+ " 'parameters': {'type': 'object',\n",
+ " 'properties': {'city': {'type': 'string',\n",
+ " 'description': 'City name to filter by (optional).'},\n",
+ " 'state_province': {'type': 'string',\n",
+ " 'description': 'State or province to filter by (optional).'},\n",
+ " 'country': {'type': 'string',\n",
+ " 'description': 'Country to filter by (optional).'},\n",
+ " 'care_type': {'type': 'string',\n",
+ " 'description': \"Service category, e.g., 'elder_care', 'child_care', 'pet_care', 'housekeeping' (optional).\"},\n",
+ " 'min_experience': {'type': 'integer',\n",
+ " 'minimum': 0,\n",
+ " 'description': 'Minimum years of experience (optional).'},\n",
+ " 'max_hourly_rate': {'type': 'number',\n",
+ " 'minimum': 0,\n",
+ " 'description': 'Maximum hourly rate in local currency (optional).'},\n",
+ " 'live_in': {'type': 'boolean',\n",
+ " 'description': 'Require live-in caregivers (optional).'},\n",
+ " 'language': {'type': 'string',\n",
+ " 'description': \"Required spoken language, e.g., 'English', 'Spanish' (optional).\"},\n",
+ " 'certification': {'type': 'string',\n",
+ " 'description': \"Required certification, e.g., 'CPR', 'CNA' (optional).\"},\n",
+ " 'day': {'type': 'string',\n",
+ " 'description': \"Day of week to match availability (optional), e.g., 'Monday', 'Tuesday', ... 'Sunday'.\"},\n",
+ " 'time_between': {'type': 'array',\n",
+ " 'description': \"Required availability window as ['HH:MM','HH:MM'] in 24h time. Matches caregivers whose availability window fully covers this range.\",\n",
+ " 'items': {'type': 'string',\n",
+ " 'pattern': '^\\\\d{2}:\\\\d{2}$',\n",
+ " 'description': \"Time in 'HH:MM' 24-hour format.\"},\n",
+ " 'minItems': 2,\n",
+ " 'maxItems': 2},\n",
+ " 'limit': {'type': 'integer',\n",
+ " 'minimum': 1,\n",
+ " 'maximum': 1000,\n",
+ " 'default': 50,\n",
+ " 'description': 'Max number of results to return (default 50).'},\n",
+ " 'offset': {'type': 'integer',\n",
+ " 'minimum': 0,\n",
+ " 'default': 0,\n",
+ " 'description': 'Number of results to skip for pagination (default 0).'}},\n",
+ " 'required': [],\n",
+ " 'additionalProperties': False}}}]"
+ ]
+ },
+ "execution_count": 66,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Tool definition schema\n",
+ "\n",
+ "tools = [{\n",
+ " \"type\": \"function\",\n",
+ " \"function\": {\n",
+ " \"name\": \"search_caregivers\",\n",
+ " \"description\": (\n",
+ " \"Flexible multi-filter caregiver search. Any filter can be omitted. \"\n",
+ " \"Supports location, service type, experience, pricing, live-in, language, \"\n",
+ " \"certifications, day/time availability, and pagination.\"\n",
+ " ),\n",
+ " \"parameters\": {\n",
+ " \"type\": \"object\",\n",
+ " \"properties\": {\n",
+ " \"city\": {\n",
+ " \"type\": \"string\",\n",
+ " \"description\": \"City name to filter by (optional).\"\n",
+ " },\n",
+ " \"state_province\": {\n",
+ " \"type\": \"string\",\n",
+ " \"description\": \"State or province to filter by (optional).\"\n",
+ " },\n",
+ " \"country\": {\n",
+ " \"type\": \"string\",\n",
+ " \"description\": \"Country to filter by (optional).\"\n",
+ " },\n",
+ " \"care_type\": {\n",
+ " \"type\": \"string\",\n",
+ " \"description\": (\n",
+ " \"Service category, e.g., 'elder_care', 'child_care', \"\n",
+ " \"'pet_care', 'housekeeping' (optional).\"\n",
+ " )\n",
+ " },\n",
+ " \"min_experience\": {\n",
+ " \"type\": \"integer\",\n",
+ " \"minimum\": 0,\n",
+ " \"description\": \"Minimum years of experience (optional).\"\n",
+ " },\n",
+ " \"max_hourly_rate\": {\n",
+ " \"type\": \"number\",\n",
+ " \"minimum\": 0,\n",
+ " \"description\": \"Maximum hourly rate in local currency (optional).\"\n",
+ " },\n",
+ " \"live_in\": {\n",
+ " \"type\": \"boolean\",\n",
+ " \"description\": \"Require live-in caregivers (optional).\"\n",
+ " },\n",
+ " \"language\": {\n",
+ " \"type\": \"string\",\n",
+ " \"description\": \"Required spoken language, e.g., 'English', 'Spanish' (optional).\"\n",
+ " },\n",
+ " \"certification\": {\n",
+ " \"type\": \"string\",\n",
+ " \"description\": \"Required certification, e.g., 'CPR', 'CNA' (optional).\"\n",
+ " },\n",
+ " \"day\": {\n",
+ " \"type\": \"string\",\n",
+ " \"description\": (\n",
+ " \"Day of week to match availability (optional), e.g., \"\n",
+ " \"'Monday', 'Tuesday', ... 'Sunday'.\"\n",
+ " )\n",
+ " },\n",
+ " \"time_between\": {\n",
+ " \"type\": \"array\",\n",
+ " \"description\": (\n",
+ " \"Required availability window as ['HH:MM','HH:MM'] in 24h time. \"\n",
+ " \"Matches caregivers whose availability window fully covers this range.\"\n",
+ " ),\n",
+ " \"items\": {\n",
+ " \"type\": \"string\",\n",
+ " \"pattern\": \"^\\\\d{2}:\\\\d{2}$\",\n",
+ " \"description\": \"Time in 'HH:MM' 24-hour format.\"\n",
+ " },\n",
+ " \"minItems\": 2,\n",
+ " \"maxItems\": 2\n",
+ " },\n",
+ " \"limit\": {\n",
+ " \"type\": \"integer\",\n",
+ " \"minimum\": 1,\n",
+ " \"maximum\": 1000,\n",
+ " \"default\": 50,\n",
+ " \"description\": \"Max number of results to return (default 50).\"\n",
+ " },\n",
+ " \"offset\": {\n",
+ " \"type\": \"integer\",\n",
+ " \"minimum\": 0,\n",
+ " \"default\": 0,\n",
+ " \"description\": \"Number of results to skip for pagination (default 0).\"\n",
+ " }\n",
+ " },\n",
+ " \"required\": [],\n",
+ " \"additionalProperties\": False\n",
+ " }\n",
+ " }\n",
+ "}]\n",
+ "\n",
+ "tools"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "76416da2",
+ "metadata": {},
+ "source": [
+ "## Step 5: Helper Functions\n",
+ "\n",
+ "**Voice:** The AI can speak its responses using OpenAI's text-to-speech.\n",
+ "\n",
+ "**Database functions:** All the queries we need—search, get profiles, check availability, etc. These are what the AI calls behind the scenes.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 67,
+ "id": "2f50cc15",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\n",
+ "# Convert text to speech using OpenAI's TTS API\n",
+ "def announcements(message):\n",
+ " response = openai.audio.speech.create(\n",
+ " model=\"gpt-4o-mini-tts\",\n",
+ " voice=\"coral\", # Also, try replacing onyx with alloy or coral\n",
+ " input=message\n",
+ " )\n",
+ " return response.content\n",
+ "\n",
+ "# Context manager for database connection\n",
+ "@contextmanager\n",
+ "def _conn(dict_rows: bool = True):\n",
+ " conn = sqlite3.connect(DB_PATH)\n",
+ " if dict_rows:\n",
+ " conn.row_factory = _dict_factory\n",
+ " try:\n",
+ " yield conn\n",
+ " conn.commit()\n",
+ " finally:\n",
+ " conn.close()\n",
+ "\n",
+ "####################\n",
+ "# Helper functions #\n",
+ "####################\n",
+ "\n",
+ "# Converts SQLite query results from tuples into dictionaries\n",
+ "def _dict_factory(cursor, row):\n",
+ " return {col[0]: row[idx] for idx, col in enumerate(cursor.description)}\n",
+ "# A debug/logging function that prints database tool activity\n",
+ "def _print(msg: str):\n",
+ " print(f\"DATABASE TOOL CALLED: {msg}\", flush=True)\n",
+ "\n",
+ "################################\n",
+ "# Caregiver database functions #\n",
+ "################################\n",
+ "\n",
+ "# Counts the number of caregivers in the database\n",
+ "def get_caregiver_count() -> int:\n",
+ " _print(\"Counting caregivers\")\n",
+ " with _conn() as conn:\n",
+ " cur = conn.cursor()\n",
+ " cur.execute(\"SELECT COUNT(*) AS n FROM caregivers\")\n",
+ " return cur.fetchone()[\"n\"]\n",
+ "\n",
+ "# Fetches a caregiver's profile by their ID\n",
+ "def get_caregiver(caregiver_id: int) -> Optional[Dict[str, Any]]:\n",
+ " _print(f\"Fetching caregiver #{caregiver_id}\")\n",
+ " with _conn() as conn:\n",
+ " cur = conn.cursor()\n",
+ " cur.execute(\"SELECT * FROM caregivers WHERE id = ?\", (caregiver_id,))\n",
+ " return cur.fetchone()\n",
+ "\n",
+ "# Lists caregivers with pagination\n",
+ "def list_caregivers(limit: int = 20, offset: int = 0) -> List[Dict[str, Any]]:\n",
+ " _print(f\"Listing caregivers (limit={limit}, offset={offset})\")\n",
+ " with _conn() as conn:\n",
+ " cur = conn.cursor()\n",
+ " cur.execute(\"\"\"\n",
+ " SELECT * FROM caregivers\n",
+ " ORDER BY id\n",
+ " LIMIT ? OFFSET ?\n",
+ " \"\"\", (limit, offset))\n",
+ " return cur.fetchall()\n",
+ "\n",
+ "# Fetches the services a caregiver offers\n",
+ "def get_services(caregiver_id: int) -> List[str]:\n",
+ " _print(f\"Fetching services for caregiver #{caregiver_id}\")\n",
+ " with _conn() as conn:\n",
+ " cur = conn.cursor()\n",
+ " cur.execute(\"\"\"\n",
+ " SELECT care_type FROM caregiver_services WHERE caregiver_id = ?\n",
+ " ORDER BY care_type\n",
+ " \"\"\", (caregiver_id,))\n",
+ " return [r[\"care_type\"] for r in cur.fetchall()]\n",
+ "\n",
+ "# Fetches the languages a caregiver speaks\n",
+ "def get_languages(caregiver_id: int) -> List[str]:\n",
+ " _print(f\"Fetching languages for caregiver #{caregiver_id}\")\n",
+ " with _conn() as conn:\n",
+ " cur = conn.cursor()\n",
+ " cur.execute(\"\"\"\n",
+ " SELECT language FROM languages WHERE caregiver_id = ?\n",
+ " ORDER BY language\n",
+ " \"\"\", (caregiver_id,))\n",
+ " return [r[\"language\"] for r in cur.fetchall()]\n",
+ "\n",
+ "# Fetches the certifications a caregiver has\n",
+ "def get_certifications(caregiver_id: int) -> List[str]:\n",
+ " _print(f\"Fetching certifications for caregiver #{caregiver_id}\")\n",
+ " with _conn() as conn:\n",
+ " cur = conn.cursor()\n",
+ " cur.execute(\"\"\"\n",
+ " SELECT cert FROM certifications WHERE caregiver_id = ?\n",
+ " ORDER BY cert\n",
+ " \"\"\", (caregiver_id,))\n",
+ " return [r[\"cert\"] for r in cur.fetchall()]\n",
+ "\n",
+ "# Fetches the traits a caregiver has\n",
+ "def get_traits(caregiver_id: int) -> List[str]:\n",
+ " _print(f\"Fetching traits for caregiver #{caregiver_id}\")\n",
+ " with _conn() as conn:\n",
+ " cur = conn.cursor()\n",
+ " cur.execute(\"\"\"\n",
+ " SELECT trait FROM traits WHERE caregiver_id = ?\n",
+ " ORDER BY trait\n",
+ " \"\"\", (caregiver_id,))\n",
+ " return [r[\"trait\"] for r in cur.fetchall()]\n",
+ "\n",
+ "# Fetches the availability of a caregiver\n",
+ "def get_availability(caregiver_id: int) -> List[Dict[str, str]]:\n",
+ " _print(f\"Fetching availability for caregiver #{caregiver_id}\")\n",
+ " with _conn() as conn:\n",
+ " cur = conn.cursor()\n",
+ " cur.execute(\"\"\"\n",
+ " SELECT day, time_start, time_end\n",
+ " FROM availability\n",
+ " WHERE caregiver_id = ?\n",
+ " ORDER BY\n",
+ " CASE day\n",
+ " WHEN 'Mon' THEN 1 WHEN 'Tue' THEN 2 WHEN 'Wed' THEN 3\n",
+ " WHEN 'Thu' THEN 4 WHEN 'Fri' THEN 5 WHEN 'Sat' THEN 6\n",
+ " WHEN 'Sun' THEN 7 ELSE 8\n",
+ " END, time_start\n",
+ " \"\"\", (caregiver_id,))\n",
+ " return cur.fetchall()\n",
+ "\n",
+ "# Fetches a caregiver's full profile\n",
+ "def get_caregiver_profile(caregiver_id: int) -> Optional[Dict[str, Any]]:\n",
+ " _print(f\"Fetching full profile for caregiver #{caregiver_id}\")\n",
+ " base = get_caregiver(caregiver_id)\n",
+ " if not base:\n",
+ " return None\n",
+ " base[\"services\"] = get_services(caregiver_id)\n",
+ " base[\"languages\"] = get_languages(caregiver_id)\n",
+ " base[\"certifications\"] = get_certifications(caregiver_id)\n",
+ " base[\"traits\"] = get_traits(caregiver_id)\n",
+ " base[\"availability\"] = get_availability(caregiver_id)\n",
+ " return base\n",
+ "\n",
+ "###########################################\n",
+ "# Search caregivers with multiple filters #\n",
+ "###########################################\n",
+ "\n",
+ "def search_caregivers(\n",
+ " city: Optional[str] = None,\n",
+ " state_province: Optional[str] = None,\n",
+ " country: Optional[str] = None,\n",
+ " care_type: Optional[str] = None,\n",
+ " min_experience: Optional[int] = None,\n",
+ " max_hourly_rate: Optional[float] = None,\n",
+ " live_in: Optional[bool] = None,\n",
+ " language: Optional[str] = None,\n",
+ " certification: Optional[str] = None,\n",
+ " day: Optional[str] = None,\n",
+ " time_between: Optional[Tuple[str, str]] = None, # ('HH:MM', 'HH:MM')\n",
+ " limit: int = 50,\n",
+ " offset: int = 0\n",
+ ") -> List[Dict[str, Any]]:\n",
+ " \"\"\"\n",
+ " Flexible multi-filter search. Any filter can be omitted.\n",
+ " \"\"\"\n",
+ " _print(\"Searching caregivers with multiple filters\")\n",
+ "\n",
+ " # base + optional joins\n",
+ " join_clauses = []\n",
+ " where = [\"1=1\"]\n",
+ " params: List[Any] = []\n",
+ "\n",
+ " if care_type:\n",
+ " join_clauses.append(\"JOIN caregiver_services s ON s.caregiver_id = c.id\")\n",
+ " where.append(\"LOWER(s.care_type) = LOWER(?)\")\n",
+ " params.append(care_type)\n",
+ "\n",
+ " if language:\n",
+ " join_clauses.append(\"JOIN languages l ON l.caregiver_id = c.id\")\n",
+ " where.append(\"LOWER(l.language) = LOWER(?)\")\n",
+ " params.append(language)\n",
+ "\n",
+ " if certification:\n",
+ " join_clauses.append(\"JOIN certifications cert ON cert.caregiver_id = c.id\")\n",
+ " where.append(\"LOWER(cert.cert) = LOWER(?)\")\n",
+ " params.append(certification)\n",
+ "\n",
+ " if day or time_between:\n",
+ " join_clauses.append(\"JOIN availability a ON a.caregiver_id = c.id\")\n",
+ " if day:\n",
+ " where.append(\"a.day = ?\")\n",
+ " params.append(day)\n",
+ " if time_between:\n",
+ " t0, t1 = time_between\n",
+ " # overlap check: caregiver window [start,end] must include [t0,t1]\n",
+ " where.append(\"a.time_start <= ? AND a.time_end >= ?\")\n",
+ " params.extend([t0, t1])\n",
+ "\n",
+ " if city:\n",
+ " where.append(\"LOWER(c.city) = LOWER(?)\")\n",
+ " params.append(city)\n",
+ " if state_province:\n",
+ " where.append(\"LOWER(c.state_province) = LOWER(?)\")\n",
+ " params.append(state_province)\n",
+ " if country:\n",
+ " where.append(\"LOWER(c.country) = LOWER(?)\")\n",
+ " params.append(country)\n",
+ " if min_experience is not None:\n",
+ " where.append(\"c.years_experience >= ?\")\n",
+ " params.append(min_experience)\n",
+ " if max_hourly_rate is not None:\n",
+ " where.append(\"c.hourly_rate <= ?\")\n",
+ " params.append(max_hourly_rate)\n",
+ " if live_in is not None:\n",
+ " where.append(\"c.live_in = ?\")\n",
+ " params.append(1 if live_in else 0)\n",
+ "\n",
+ " sql = f\"\"\"\n",
+ " SELECT DISTINCT c.*\n",
+ " FROM caregivers c\n",
+ " {' '.join(join_clauses)}\n",
+ " WHERE {' AND '.join(where)}\n",
+ " ORDER BY c.hourly_rate ASC, c.years_experience DESC, c.id\n",
+ " LIMIT ? OFFSET ?\n",
+ " \"\"\"\n",
+ " params.extend([limit, offset])\n",
+ "\n",
+ " with _conn() as conn:\n",
+ " cur = conn.cursor()\n",
+ " cur.execute(sql, tuple(params))\n",
+ " return cur.fetchall()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6c526d05",
+ "metadata": {},
+ "source": [
+ "## Step 6: Quick Test\n",
+ "\n",
+ "Before connecting everything to the AI, let's make sure the database works. Run these examples to see sample caregivers and their profiles.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 68,
+ "id": "98165a21",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "DATABASE TOOL CALLED: Searching caregivers with multiple filters\n",
+ "Found 1 elder care providers in New York:\n",
+ "- Grace Williams: $28.0/hr, 6 years experience\n",
+ "\n",
+ "============================================================\n",
+ "\n",
+ "DATABASE TOOL CALLED: Searching caregivers with multiple filters\n",
+ "Found 1 Spanish-speaking child care providers:\n",
+ "- Isabella Garcia in San Jose, CA\n",
+ "\n",
+ "============================================================\n",
+ "\n",
+ "DATABASE TOOL CALLED: Fetching full profile for caregiver #1\n",
+ "DATABASE TOOL CALLED: Fetching caregiver #1\n",
+ "DATABASE TOOL CALLED: Fetching services for caregiver #1\n",
+ "DATABASE TOOL CALLED: Fetching languages for caregiver #1\n",
+ "DATABASE TOOL CALLED: Fetching certifications for caregiver #1\n",
+ "DATABASE TOOL CALLED: Fetching traits for caregiver #1\n",
+ "DATABASE TOOL CALLED: Fetching availability for caregiver #1\n",
+ "Detailed profile for Grace Williams:\n",
+ " Services: companionship, elder care\n",
+ " Languages: English, Spanish\n",
+ " Certifications: CPR, First Aid\n",
+ " Traits: detail-oriented, empathetic\n",
+ " Availability: 3 time slots\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Example 1: Search for elder care providers in New York\n",
+ "results = search_caregivers(\n",
+ " city=\"New York\",\n",
+ " care_type=\"elder care\",\n",
+ " max_hourly_rate=30.0,\n",
+ " limit=5\n",
+ ")\n",
+ "\n",
+ "print(f\"Found {len(results)} elder care providers in New York:\")\n",
+ "for caregiver in results:\n",
+ " print(f\"- {caregiver['name']}: ${caregiver['hourly_rate']}/hr, {caregiver['years_experience']} years experience\")\n",
+ "\n",
+ "print(\"\\n\" + \"=\"*60 + \"\\n\")\n",
+ "\n",
+ "# Example 2: Search for Spanish-speaking child care providers\n",
+ "results2 = search_caregivers(\n",
+ " care_type=\"child care\",\n",
+ " language=\"Spanish\",\n",
+ " limit=3\n",
+ ")\n",
+ "\n",
+ "print(f\"Found {len(results2)} Spanish-speaking child care providers:\")\n",
+ "for caregiver in results2:\n",
+ " print(f\"- {caregiver['name']} in {caregiver['city']}, {caregiver['state_province']}\")\n",
+ "\n",
+ "print(\"\\n\" + \"=\"*60 + \"\\n\")\n",
+ "\n",
+ "# Example 3: Get detailed profile of a specific caregiver\n",
+ "if results:\n",
+ " caregiver_id = results[0]['id']\n",
+ " profile = get_caregiver_profile(caregiver_id)\n",
+ " print(f\"Detailed profile for {profile['name']}:\")\n",
+ " print(f\" Services: {', '.join(profile['services'])}\")\n",
+ " print(f\" Languages: {', '.join(profile['languages'])}\")\n",
+ " print(f\" Certifications: {', '.join(profile['certifications'])}\")\n",
+ " print(f\" Traits: {', '.join(profile['traits'])}\")\n",
+ " print(f\" Availability: {len(profile['availability'])} time slots\")\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "abfa81e6",
+ "metadata": {},
+ "source": [
+ "## Step 7: The AI's Instructions\n",
+ "\n",
+ "Here's where I learned prompt engineering matters *a lot*.\n",
+ "\n",
+ "The AI needs to know:\n",
+ "- What exact keywords to use (\"elder care\" not \"elderly care\", \"Mon\" not \"Monday\")\n",
+ "- How to map natural language to database values\n",
+ "- That it should give 2-3 recommendations with pros/cons\n",
+ "- To remind families to verify credentials independently\n",
+ "\n",
+ "**The lesson from MyWoosah:** Small keyword mismatches = zero results. This prompt prevents that.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 69,
+ "id": "7bbe36e3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# System prompt\n",
+ "\n",
+ "system_prompt = '''\n",
+ " You are a compassionate Caregiver Assistant that helps families quickly identify the most\n",
+ " suitable care provider by gathering requirements (care needs, schedule, budget, location,\n",
+ " language/cultural fit) and matching them to available profiles. Provide 2-3 best-fit options\n",
+ " with pros/cons, estimated costs, and next steps, and clearly state that credentials/background\n",
+ " checks are not verified by this sample app and should be confirmed by the family.\n",
+ "\n",
+ " CRITICAL: When searching the database, you MUST use these EXACT terms:\n",
+ "\n",
+ " CARE TYPES (use exactly as shown):\n",
+ " - \"elder care\" (for elderly, senior, old age, geriatric care)\n",
+ " - \"companionship\" (for companion, friendship, social support)\n",
+ " - \"post-op support\" (for post-surgery, post-operative, recovery care)\n",
+ " - \"child care\" (for children, kids, babysitting, nanny)\n",
+ " - \"special needs\" (for disabilities, autism, developmental needs)\n",
+ " - \"respite care\" (for temporary relief, break for family caregivers)\n",
+ " - \"dementia care\" (for Alzheimer's, memory care, cognitive decline)\n",
+ " - \"hospice support\" (for end-of-life, palliative, terminal care)\n",
+ "\n",
+ " If a user mentions any variation, map it to the closest match above. If unclear, ask clarifying questions.\n",
+ "\n",
+ " DAYS OF WEEK (use exactly as shown):\n",
+ " - \"Mon\" (for Monday)\n",
+ " - \"Tue\" (for Tuesday)\n",
+ " - \"Wed\" (for Wednesday)\n",
+ " - \"Thu\" (for Thursday)\n",
+ " - \"Fri\" (for Friday)\n",
+ " - \"Sat\" (for Saturday)\n",
+ " - \"Sun\" (for Sunday)\n",
+ "\n",
+ " STATES/PROVINCES (use 2-letter codes):\n",
+ " - Use standard US state abbreviations: \"NY\", \"CA\", \"TX\", \"FL\", \"MA\", etc.\n",
+ " - Convert full state names to abbreviations before searching\n",
+ "\n",
+ " COMMON LANGUAGES:\n",
+ " - \"English\", \"Spanish\", \"French\", \"Vietnamese\", \"Korean\", \"Hindi\", \"Mandarin\", \"Portuguese\", \"Tagalog\", \"ASL\"\n",
+ " - Capitalize properly (e.g., user says \"spanish\" → use \"Spanish\")\n",
+ "\n",
+ " CERTIFICATIONS:\n",
+ " - \"CPR\", \"First Aid\", \"CNA\", \"BLS\", \"HHA\", \"AED\", \"Medication Technician\", \"Hospice Training\", \n",
+ " \"Dementia Care\", \"Special Needs Training\", \"Childcare Safety\"\n",
+ " - Use exact capitalization and full names\n",
+ "\n",
+ " TRAITS:\n",
+ " - \"empathetic\", \"patient\", \"cheerful\", \"organized\", \"compassionate\", \"calm under pressure\", \n",
+ " \"adaptable\", \"friendly\", \"thorough\", \"gentle\", \"proactive\", \"flexible\", \"reliable\", \n",
+ " \"detail-oriented\", \"communicative\", \"energetic\", \"respectful\", \"dependable\", \"attentive\", \n",
+ " \"kind\", \"tidy\", \"punctual\", \"discreet\", \"professional\", \"trustworthy\"\n",
+ " - Use lowercase for all traits\n",
+ "\n",
+ " SEARCH STRATEGY:\n",
+ " 1. Listen carefully to user requirements\n",
+ " 2. Map their natural language to database terms above\n",
+ " 3. Use search_caregivers() with exact keyword matches\n",
+ " 4. If no results, suggest alternatives or broader searches\n",
+ " 5. After getting results, use get_caregiver_profile() for detailed information on top matches\n",
+ "\n",
+ " Always confirm your understanding by restating requirements using the exact database terms before searching.\n",
+ "'''"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0b8ae902",
+ "metadata": {},
+ "source": [
+ "## Step 8: Making it Work (and Not Crash)\n",
+ "\n",
+ "This is the engine room. When the AI wants to search, this code:\n",
+ "1. Validates the request\n",
+ "2. Calls the right database function\n",
+ "3. Handles errors gracefully (no crashes!)\n",
+ "4. Limits results to prevent overwhelming the AI\n",
+ "5. Generates the voice response\n",
+ "\n",
+ "**Defensive programming:** I learned the hard way that things break. This code expects problems and handles them elegantly.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 70,
+ "id": "0d8accbc",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Function registry: Maps tool names to actual Python functions\n",
+ "TOOL_REGISTRY = {\n",
+ " \"search_caregivers\": search_caregivers,\n",
+ " \"get_caregiver_count\": get_caregiver_count,\n",
+ " \"get_caregiver\": get_caregiver,\n",
+ " \"list_caregivers\": list_caregivers,\n",
+ " \"get_services\": get_services,\n",
+ " \"get_languages\": get_languages,\n",
+ " \"get_certifications\": get_certifications,\n",
+ " \"get_traits\": get_traits,\n",
+ " \"get_availability\": get_availability,\n",
+ " \"get_caregiver_profile\": get_caregiver_profile,\n",
+ "}\n",
+ "\n",
+ "def execute_tool_call(tool_call):\n",
+ " \"\"\"\n",
+ " Safely execute a single tool call with error handling.\n",
+ " Returns a properly formatted tool response.\n",
+ " \"\"\"\n",
+ " import json\n",
+ " \n",
+ " function_name = tool_call.function.name\n",
+ " \n",
+ " # Defensive check: Ensure function exists in registry\n",
+ " if function_name not in TOOL_REGISTRY:\n",
+ " return {\n",
+ " \"role\": \"tool\",\n",
+ " \"tool_call_id\": tool_call.id,\n",
+ " \"content\": json.dumps({\n",
+ " \"error\": f\"Unknown function: {function_name}\",\n",
+ " \"available_functions\": list(TOOL_REGISTRY.keys())\n",
+ " })\n",
+ " }\n",
+ " \n",
+ " try:\n",
+ " # Parse arguments\n",
+ " args = json.loads(tool_call.function.arguments)\n",
+ " \n",
+ " # Execute the function\n",
+ " func = TOOL_REGISTRY[function_name]\n",
+ " result = func(**args)\n",
+ " \n",
+ " # Format response based on result type with limit to prevent token overflow\n",
+ " if isinstance(result, list):\n",
+ " content = json.dumps({\n",
+ " \"count\": len(result),\n",
+ " \"results\": result[:10] if len(result) > 10 else result,\n",
+ " \"truncated\": len(result) > 10\n",
+ " })\n",
+ " elif isinstance(result, dict):\n",
+ " content = json.dumps(result)\n",
+ " elif isinstance(result, (int, float, str)):\n",
+ " content = json.dumps({\"result\": result})\n",
+ " else:\n",
+ " content = str(result)\n",
+ " \n",
+ " return {\n",
+ " \"role\": \"tool\",\n",
+ " \"tool_call_id\": tool_call.id,\n",
+ " \"content\": content\n",
+ " }\n",
+ " \n",
+ " except Exception as e:\n",
+ " # Defensive error handling\n",
+ " return {\n",
+ " \"role\": \"tool\",\n",
+ " \"tool_call_id\": tool_call.id,\n",
+ " \"content\": json.dumps({\n",
+ " \"error\": str(e),\n",
+ " \"function\": function_name,\n",
+ " \"args\": tool_call.function.arguments\n",
+ " })\n",
+ " }\n",
+ "\n",
+ "def process_tool_calls(message):\n",
+ " \"\"\"\n",
+ " Process all tool calls from the AI response.\n",
+ " Returns tool responses and extracted metadata.\n",
+ " \"\"\"\n",
+ " responses = []\n",
+ " metadata = {\n",
+ " \"cities\": set(),\n",
+ " \"caregiver_ids\": set(),\n",
+ " \"total_results\": 0\n",
+ " }\n",
+ " \n",
+ " if not message.tool_calls:\n",
+ " return responses, metadata\n",
+ " \n",
+ " for tool_call in message.tool_calls:\n",
+ " # Execute the tool call\n",
+ " response = execute_tool_call(tool_call)\n",
+ " responses.append(response)\n",
+ " \n",
+ " # Extract metadata for UI enhancements\n",
+ " try:\n",
+ " import json\n",
+ " content = json.loads(response[\"content\"])\n",
+ " \n",
+ " # Extract cities from search results\n",
+ " if \"results\" in content and isinstance(content[\"results\"], list):\n",
+ " for item in content[\"results\"]:\n",
+ " if isinstance(item, dict) and \"city\" in item:\n",
+ " metadata[\"cities\"].add(item[\"city\"])\n",
+ " if isinstance(item, dict) and \"id\" in item:\n",
+ " metadata[\"caregiver_ids\"].add(item[\"id\"])\n",
+ " \n",
+ " if \"count\" in content:\n",
+ " metadata[\"total_results\"] += content[\"count\"]\n",
+ " \n",
+ " except:\n",
+ " pass # Silently ignore metadata extraction errors\n",
+ " \n",
+ " return responses, metadata\n",
+ "\n",
+ "def generate_city_image(city):\n",
+ " \"\"\"\n",
+ " Generate or retrieve a city image (placeholder for future enhancement).\n",
+ " Could integrate with DALL-E, Unsplash API, or local image database.\n",
+ " \"\"\"\n",
+ " # Placeholder - can be enhanced with actual image generation\n",
+ " return None\n",
+ "\n",
+ "def chat(history):\n",
+ " \"\"\"\n",
+ " Main chat handler with multi-modal support and defensive error handling.\n",
+ " Handles conversation flow, tool calls, and response generation.\n",
+ " \"\"\"\n",
+ " # Normalize history format\n",
+ " history = [{\"role\": h[\"role\"], \"content\": h[\"content\"]} for h in history]\n",
+ " \n",
+ " # Initialize conversation with system prompt\n",
+ " messages = [{\"role\": \"system\", \"content\": system_prompt}] + history\n",
+ " \n",
+ " # Initialize metadata\n",
+ " image = None\n",
+ " selected_city = None\n",
+ " \n",
+ " try:\n",
+ " # Initial API call\n",
+ " response = openai.chat.completions.create(\n",
+ " model=MODEL,\n",
+ " messages=messages,\n",
+ " tools=tools\n",
+ " )\n",
+ " \n",
+ " # Tool calling loop (with safety limit)\n",
+ " max_iterations = 5\n",
+ " iteration = 0\n",
+ " \n",
+ " while response.choices[0].finish_reason == \"tool_calls\" and iteration < max_iterations:\n",
+ " iteration += 1\n",
+ " message = response.choices[0].message\n",
+ " \n",
+ " # Process all tool calls\n",
+ " tool_responses, metadata = process_tool_calls(message)\n",
+ " \n",
+ " # Track city for image generation\n",
+ " if metadata[\"cities\"]:\n",
+ " selected_city = list(metadata[\"cities\"])[0]\n",
+ " \n",
+ " # Add assistant message and tool responses to conversation\n",
+ " messages.append(message)\n",
+ " messages.extend(tool_responses)\n",
+ " \n",
+ " # Continue conversation\n",
+ " response = openai.chat.completions.create(\n",
+ " model=MODEL,\n",
+ " messages=messages,\n",
+ " tools=tools\n",
+ " )\n",
+ " \n",
+ " # Extract final reply\n",
+ " reply = response.choices[0].message.content\n",
+ " history.append({\"role\": \"assistant\", \"content\": reply})\n",
+ " \n",
+ " # Generate voice response\n",
+ " voice = announcements(reply)\n",
+ " \n",
+ " # Generate city image if applicable\n",
+ " if selected_city:\n",
+ " image = generate_city_image(selected_city)\n",
+ " \n",
+ " return history, voice, image\n",
+ " \n",
+ " except Exception as e:\n",
+ " # Defensive error handling for the entire chat flow\n",
+ " error_message = f\"I apologize, but I encountered an error: {str(e)}. Please try again.\"\n",
+ " history.append({\"role\": \"assistant\", \"content\": error_message})\n",
+ " return history, None, None"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "451ed2e5",
+ "metadata": {},
+ "source": [
+ "## Step 9: The Interface\n",
+ "\n",
+ "A clean, professional web UI built with Gradio.\n",
+ "\n",
+ "Features:\n",
+ "- Chat interface with conversation history\n",
+ "- Voice responses that auto-play\n",
+ "- Settings sidebar (model selection, voice options)\n",
+ "- Clear instructions for families\n",
+ "\n",
+ "**Why Gradio?** At MyWoosah, I needed something non-technical staff could use immediately. Gradio made that possible without weeks of frontend work.\n",
+ "\n",
+ "**Run this cell to launch!** 🚀\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 71,
+ "id": "a07e7793-b8f5-44f4-aded-5562f633271a",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "* Running on local URL: http://127.0.0.1:7871\n",
+ "* To create a public link, set `share=True` in `launch()`.\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": []
+ },
+ "execution_count": 71,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import gradio as gr\n",
+ "\n",
+ "# Gradio UI Setup\n",
+ "\n",
+ "def put_message_in_chatbot(message, history):\n",
+ " \"\"\"Add user message to chat history\"\"\"\n",
+ " return \"\", history + [{\"role\": \"user\", \"content\": message}]\n",
+ "\n",
+ "# Custom CSS for better styling\n",
+ "custom_css = \"\"\"\n",
+ "#chatbot {\n",
+ " border-radius: 10px;\n",
+ " box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n",
+ "}\n",
+ "#message_box {\n",
+ " border-radius: 8px;\n",
+ "}\n",
+ ".header {\n",
+ " text-align: center;\n",
+ " padding: 20px;\n",
+ " background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n",
+ " color: white;\n",
+ " border-radius: 10px;\n",
+ " margin-bottom: 20px;\n",
+ "}\n",
+ "\"\"\"\n",
+ "\n",
+ "with gr.Blocks(title=\"CareGiver AI Assistant\", css=custom_css, theme=gr.themes.Soft()) as ui:\n",
+ " \n",
+ " # Header\n",
+ " gr.Markdown(\"\"\"\n",
+ " \n",
+ " \"\"\")\n",
+ " \n",
+ " # Instructions\n",
+ " with gr.Accordion(\"ℹ️ Click here to learn more on how to use this AI\", open=False):\n",
+ " gr.Markdown(\"\"\"\n",
+ " **Tell me what you need:**\n",
+ " - Type of care (elder care, child care, companionship, etc.)\n",
+ " - Location (city, state)\n",
+ " - Schedule requirements (days/times)\n",
+ " - Budget constraints\n",
+ " - Language or certification needs\n",
+ " \n",
+ " **Example:** \"I need an elder care provider in Boston for Monday mornings who speaks Spanish and has CPR certification.\"\n",
+ " \n",
+ " ⚠️ **Note:** This is a demo app. Always verify credentials and conduct background checks independently.\n",
+ " \"\"\")\n",
+ " \n",
+ " # Main chat interface\n",
+ " with gr.Row():\n",
+ " with gr.Column(scale=2):\n",
+ " chatbot = gr.Chatbot(\n",
+ " height=500, \n",
+ " type=\"messages\",\n",
+ " elem_id=\"chatbot\",\n",
+ " label=\"Chat History\",\n",
+ " avatar_images=(None, \"🤖\")\n",
+ " )\n",
+ " \n",
+ " # Audio output\n",
+ " audio_output = gr.Audio(\n",
+ " label=\"Voice Response\",\n",
+ " autoplay=True,\n",
+ " visible=True,\n",
+ " interactive=False\n",
+ " )\n",
+ " \n",
+ " # Settings sidebar\n",
+ " with gr.Column(scale=1):\n",
+ " gr.Markdown(\"### ⚙️ Settings\")\n",
+ " \n",
+ " # Model selector (for future enhancement)\n",
+ " model_select = gr.Dropdown(\n",
+ " choices=[\"gpt-4o-mini\", \"gpt-4o\", \"gpt-4-turbo\"],\n",
+ " value=\"gpt-4o-mini\",\n",
+ " label=\"AI Model\",\n",
+ " interactive=True\n",
+ " )\n",
+ " \n",
+ " # Voice selector\n",
+ " voice_select = gr.Dropdown(\n",
+ " choices=[\"coral\", \"alloy\", \"echo\", \"fable\", \"onyx\", \"nova\", \"shimmer\"],\n",
+ " value=\"coral\",\n",
+ " label=\"Voice\",\n",
+ " interactive=True\n",
+ " )\n",
+ " \n",
+ " # Audio toggle\n",
+ " audio_enabled = gr.Checkbox(\n",
+ " label=\"Enable Voice Responses\",\n",
+ " value=True\n",
+ " )\n",
+ " \n",
+ " # Clear button\n",
+ " clear_btn = gr.Button(\"🗑️ Clear Conversation\", variant=\"secondary\")\n",
+ " \n",
+ " # Input section\n",
+ " with gr.Row():\n",
+ " message = gr.Textbox(\n",
+ " label=\"Your Message\",\n",
+ " placeholder=\"Type your question here... (e.g., 'I need elder care in Boston')\",\n",
+ " lines=2,\n",
+ " elem_id=\"message_box\",\n",
+ " scale=4\n",
+ " )\n",
+ " send_btn = gr.Button(\"Send\", variant=\"primary\", scale=1)\n",
+ " \n",
+ " # Event handlers\n",
+ " def chat_wrapper(history):\n",
+ " \"\"\"Wrapper to handle chat and extract only needed outputs\"\"\"\n",
+ " history_out, voice, image = chat(history)\n",
+ " return history_out, voice\n",
+ " \n",
+ " # Submit on enter or button click\n",
+ " submit_event = message.submit(\n",
+ " put_message_in_chatbot,\n",
+ " inputs=[message, chatbot],\n",
+ " outputs=[message, chatbot]\n",
+ " ).then(\n",
+ " chat_wrapper,\n",
+ " inputs=chatbot,\n",
+ " outputs=[chatbot, audio_output]\n",
+ " )\n",
+ " \n",
+ " send_btn.click(\n",
+ " put_message_in_chatbot,\n",
+ " inputs=[message, chatbot],\n",
+ " outputs=[message, chatbot]\n",
+ " ).then(\n",
+ " chat_wrapper,\n",
+ " inputs=chatbot,\n",
+ " outputs=[chatbot, audio_output]\n",
+ " )\n",
+ " \n",
+ " # Clear conversation\n",
+ " clear_btn.click(\n",
+ " lambda: ([], None),\n",
+ " outputs=[chatbot, audio_output]\n",
+ " )\n",
+ " \n",
+ " # Footer\n",
+ " gr.Markdown(\"\"\"\n",
+ " ---\n",
+ " \n",
+ " Powered by OpenAI & Gradio | Built by RoboOffice Ltd\n",
+ " \n",
+ " \"\"\")\n",
+ "\n",
+ "# Launch with better configuration\n",
+ "ui.launch(\n",
+ " inbrowser=True,\n",
+ " share=False,\n",
+ " show_error=True,\n",
+ " quiet=False\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "97d87d95",
+ "metadata": {},
+ "source": [
+ "---\n",
+ "\n",
+ "## Reflections\n",
+ "\n",
+ "This project started from frustration: *\"There has to be a better way to match families with caregivers.\"*\n",
+ "\n",
+ "Through the Andela program, I learned that AI + thoughtful engineering = solutions to real problems.\n",
+ "\n",
+ "### What Worked:\n",
+ "- **Function calling** eliminated the need for custom queries\n",
+ "- **Prompt engineering** prevented keyword mismatches\n",
+ "- **Defensive coding** made it robust\n",
+ "- **Gradio** made it accessible\n",
+ "\n",
+ "### What I'd Do Next:\n",
+ "- Add speech input (families could call and talk)\n",
+ "- Connect to actual MyWoosah database\n",
+ "- Add background check API integration\n",
+ "- Deploy for real users\n",
+ "\n",
+ "### The Bigger Picture:\n",
+ "\n",
+ "This isn't just about caregiving. The same pattern works for:\n",
+ "- Healthcare appointments\n",
+ "- Legal services\n",
+ "- Tutoring platforms\n",
+ "- Any matching problem where natural language beats forms\n",
+ "\n",
+ "AI doesn't replace good database design—it makes it accessible to everyone.\n",
+ "\n",
+ "---\n",
+ "\n",
+ "**For MyWoosah Inc and beyond:** This is proof that AI can transform how we connect people with the care they need.\n",
+ "\n",
+ "*Built during Week 2 of the Andela LLM Engineering Program*\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": ".venv",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.12.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}