# 03 — Self-Hosted AI Layer

## Context
Fogbreak replaces Follow Up Boss, ShowingTime, Sisu, Paperless Pipeline, DocuSign, and RealScout. The AI layer powers: chatbot, lead scoring, copy generation, coaching, market analysis, Fair Housing audit, and property matching. Self-hosted = free, no API costs.

## What You're Doing
1. Install Ollama locally for model serving
2. Build a Python FastAPI proxy that Fogbreak's PHP backend calls
3. Create agent definitions for each use case
4. Connect the admin.html AI panel stub to the live backend
5. Build the abstraction so models can be swapped without code changes

## Architecture

```
PHP Backend (existing) → HTTP → FastAPI AI Proxy → Ollama (local models)
                                    ↓
                              Agent Framework
                           (LangGraph / CrewAI)
                                    ↓
                         Specialized Agents:
                         - Chatbot (client-facing)
                         - Lead Scorer
                         - Copywriter (listings, social, email)
                         - Coach (agent performance)
                         - Market Analyst (CMAs, trends)
                         - Fair Housing Auditor
                         - Property Matcher (RealScout replacement)
                         - TC Assistant (Paperless Pipeline helper)
                         - Showing Coordinator (ShowingTime helper)
```

## Step-by-Step

### Step 1: Install Ollama and Models

```bash
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh

# Pull models (free, open-source)
ollama pull llama3.3:70b    # Heavy reasoning: deal analysis, CMAs, coaching
ollama pull mistral:7b      # Fast/lightweight: chatbot, FAQ, routine tasks
ollama pull nomic-embed-text # Embeddings for RAG (property matching, search)
```

### Step 2: Create AI Service Directory

```
ai/
├── server.py               # FastAPI main server
├── config.py               # Model configs, prompts path
├── requirements.txt        # Python dependencies
├── agents/
│   ├── __init__.py
│   ├── base.py             # Base agent class
│   ├── chatbot.py          # Client-facing chatbot
│   ├── lead_scorer.py      # AI lead scoring (replaces FUB scoring)
│   ├── copywriter.py       # Listing/social/email copy generation
│   ├── coach.py            # Agent coaching (replaces Sisu coaching)
│   ├── market_analyst.py   # CMA generation, market reports
│   ├── fair_housing.py     # Compliance audit on all outbound content
│   ├── property_matcher.py # Smart matching (replaces RealScout)
│   ├── tc_assistant.py     # TC workflow helper (replaces Paperless Pipeline AI)
│   └── showing_coordinator.py # Showing scheduling AI (replaces ShowingTime AI)
├── prompts/
│   ├── system/             # Base system prompts
│   ├── market/             # Market-specific prompt overrides (loaded from DB)
│   └── brand/              # Brand voice prompts per tenant
├── rag/
│   ├── embeddings.py       # Generate/store embeddings
│   ├── retriever.py        # Similarity search
│   └── indexer.py          # Index properties, docs, market data
└── models/
    ├── ollama_client.py    # Ollama HTTP client
    └── model_router.py     # Route requests to appropriate model
```

### Step 3: Build FastAPI Server

`ai/server.py`:
```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import httpx

app = FastAPI(title="Fogbreak AI", version="1.0")

OLLAMA_URL = "http://localhost:11434"

class ChatRequest(BaseModel):
    messages: list[dict]
    model: str = "mistral:7b"
    agent: str = "chatbot"
    context: dict = {}
    tenant_id: int = None
    market_id: int = None

class GenerateRequest(BaseModel):
    prompt: str
    model: str = "mistral:7b"
    agent: str = "copywriter"
    context: dict = {}

@app.post("/api/ai/chat")
async def chat(req: ChatRequest):
    # Load agent-specific system prompt
    # Load market-specific prompt overrides from DB
    # Load brand voice from tenant config
    # Call Ollama
    # Return response

@app.post("/api/ai/generate")
async def generate(req: GenerateRequest):
    # For one-shot generation (listing copy, social posts, etc.)

@app.post("/api/ai/score-lead")
async def score_lead(data: dict):
    # AI-powered lead scoring

@app.post("/api/ai/match-properties")
async def match_properties(data: dict):
    # Smart property matching (RealScout replacement)

@app.post("/api/ai/audit-fair-housing")
async def audit_fair_housing(data: dict):
    # Fair Housing compliance check on text

@app.post("/api/ai/coaching-insights")
async def coaching_insights(data: dict):
    # Generate coaching recommendations from agent metrics

@app.post("/api/ai/tc-suggest")
async def tc_suggest(data: dict):
    # TC workflow suggestions

@app.post("/api/ai/showing-optimize")
async def showing_optimize(data: dict):
    # Optimize showing routes and scheduling

@app.get("/api/ai/health")
async def health():
    # Check Ollama is running, models loaded
```

### Step 4: Build Model Router

`ai/models/model_router.py`:
```python
MODEL_ROUTING = {
    # Heavy reasoning tasks → large model
    "market_analyst": "llama3.3:70b",
    "coach": "llama3.3:70b",
    "tc_assistant": "llama3.3:70b",

    # Fast/routine tasks → small model
    "chatbot": "mistral:7b",
    "copywriter": "mistral:7b",
    "lead_scorer": "mistral:7b",
    "fair_housing": "mistral:7b",
    "property_matcher": "mistral:7b",
    "showing_coordinator": "mistral:7b",
}
```

### Step 5: Build PHP Integration

Create `app/api/ai.php`:
```php
<?php
require_once 'config.php';

define('AI_SERVICE_URL', 'http://localhost:8000');

function aiChat($messages, $agent = 'chatbot', $context = []) {
    $payload = json_encode([
        'messages' => $messages,
        'agent' => $agent,
        'context' => $context,
        'tenant_id' => $_SESSION['tenant_id'] ?? null,
        'market_id' => $_SESSION['market_id'] ?? null,
    ]);

    $ch = curl_init(AI_SERVICE_URL . '/api/ai/chat');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $payload,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 120,
    ]);
    $response = curl_exec($ch);
    curl_close($ch);
    return json_decode($response, true);
}

function aiGenerate($prompt, $agent = 'copywriter', $context = []) {
    // Similar to aiChat but for one-shot generation
}

function aiScoreLead($leadData) {
    // Call /api/ai/score-lead
}

function aiMatchProperties($searchCriteria, $clientPreferences) {
    // Call /api/ai/match-properties (RealScout replacement)
}

function aiAuditFairHousing($text) {
    // Call /api/ai/audit-fair-housing
}

function aiCoachingInsights($agentId, $metrics) {
    // Call /api/ai/coaching-insights (Sisu replacement)
}

// Route by action
$action = $_GET['action'] ?? '';
switch ($action) {
    case 'chat': /* handle chat */ break;
    case 'generate': /* handle generate */ break;
    case 'score_lead': /* handle lead scoring */ break;
    case 'match_properties': /* handle property matching */ break;
    case 'audit': /* handle Fair Housing audit */ break;
    case 'coaching': /* handle coaching insights */ break;
    case 'tc_suggest': /* handle TC suggestions */ break;
    case 'showing_optimize': /* handle showing optimization */ break;
}
```

### Step 6: Connect admin.html AI Panel

The `admin.html` already has a Claude AI panel stub at line ~2463. Wire it up:

1. Replace the static UI with a working chat interface
2. Connect to `/api/ai.php?action=chat`
3. Support context injection (current client, current deal, current listing)
4. Add agent switcher (chatbot, analyst, copywriter, coach)
5. Show typing indicators and streaming responses

### Step 7: Add AI to Every Module

Integrate AI calls into existing modules:
- `admin.php` → AI-powered social post generation (replace template strings)
- `drips.php` → AI-generated email content per drip step
- `properties.php` → AI property matching scores
- `transactions.php` → AI compliance suggestions based on market
- `email.php` → AI Fair Housing audit (upgrade existing audit)
- `commissions.php` → AI-powered GCI forecasting

### Step 8: Build RAG for Property Knowledge

```python
# ai/rag/indexer.py
# Index all properties into vector embeddings
# When client searches, use semantic similarity not just field matching
# This is what makes Fogbreak's property matching better than RealScout
```

## Requirements.txt
```
fastapi>=0.104
uvicorn>=0.24
httpx>=0.25
pydantic>=2.5
langchain>=0.1
langgraph>=0.0.20
crewai>=0.1
chromadb>=0.4
numpy>=1.24
```

## Running the AI Service
```bash
cd ai/
pip install -r requirements.txt
uvicorn server:app --host 0.0.0.0 --port 8000 --reload
```

## Acceptance Criteria
- [ ] Ollama installed with Llama 3.3 70B and Mistral 7B pulled
- [ ] FastAPI server running on port 8000
- [ ] All agent endpoints functional (/chat, /generate, /score-lead, /match-properties, /audit-fair-housing, /coaching-insights, /tc-suggest, /showing-optimize)
- [ ] Model router correctly directs heavy tasks to large model, routine to small
- [ ] PHP ai.php module calls FastAPI and returns results
- [ ] admin.html AI panel is live and functional
- [ ] AI responses are market-aware (load prompts from database)
- [ ] AI responses are brand-aware (load brand voice from tenant config)
- [ ] Fair Housing audit runs on all outbound content
- [ ] RAG indexes properties for semantic search
