# CBP Chat — Co-opbank Pertama AI Chatbot

**Domain:** `cbp-chat.weststar-dev.com` · **Replaces:** the old menu-driven Co-opbank Chat (quick-reply bot)
**Stack:** Laravel 12 · Tailwind CSS · MariaDB (phpMyAdmin) · [Weststar AI Agents API](https://api.weststar-ai.com/docs/agents/)

---

## 1. Goal

Replace the legacy rule/menu chatbot with a real AI assistant ("CBP AI", fronted by the **Chombee** mascot) that answers Co-opbank Pertama questions from a managed knowledge base (RAG), with:

1. **Visitor flow** — register (full name + email) → email OTP verification (branded email) → chat with CBP AI.
2. **`/admin`** — manage the agent's **RAG documents**, **agent answers/behaviour** (system prompt, quick replies, fallback text), and **agent profile** (name, model, temperature, top_k) — all via the Agents API.

---

## 2. External API — Weststar AI Agents

Base: `https://api.weststar-ai.com/v1/agents` · Auth: `Authorization: Bearer <key>`
(Agents are private per API key. Embeddings: nomic-embed-text; storage Postgres+pgvector — server side, nothing for us to host.)

| Purpose | Endpoint |
|---|---|
| Create agent | `POST /v1/agents` — `name`, `system_prompt`, `model` (default `qwen2.5:3b`), `temperature` (0.3), `top_k` (5), `guardrails` |
| List / get / update / delete | `GET /v1/agents`, `GET|PATCH|DELETE /v1/agents/{id}` |
| Add doc by URL | `POST /v1/agents/{id}/documents` — `{url, title}` (PDF & web pages auto-chunked) |
| Upload local file | `POST /v1/agents/{id}/documents/upload` — multipart `file` |
| List docs | `GET /v1/agents/{id}/documents` — status + chunk counts |
| Chat | `POST /v1/agents/{id}/chat` — `{"messages":[{role,content},…]}` → `{answer, citations[], tool_trace[], model}` |
| Tools / audit | `POST|GET /v1/agents/{id}/tools`, `GET /v1/agents/{id}/audit` (view-only in admin, phase 2) |

**Notes / risks**
- No documented *delete document* endpoint — admin UI must tolerate that (hide/disable delete or attempt `DELETE /documents/{id}` and degrade gracefully).
- `answer` contains inline `[n]` citations — render citation chips from `citations[]`, or strip markers if no sources.
- **API key is not in the repo.** Key + agent id live in a DB `settings` table, editable in `/admin` → Settings. App boots and deploys without the key; chat shows a friendly "not configured" state until it's pasted in.

### Laravel service layer

`app/Services/AgentsApi.php` — thin Guzzle/Http client wrapper: `agents()`, `agent($id)`, `createAgent()`, `updateAgent()`, `documents($id)`, `addDocumentUrl()`, `uploadDocument()`, `chat($id, array $messages)`. Reads key/base from `settings` (DB) with `.env` fallback (`WESTSTAR_AI_KEY`, `WESTSTAR_AI_BASE`). Timeouts: 60 s chat, 120 s upload. All failures logged; user sees a polite BM fallback message.

---

## 3. Architecture

```
Browser (visitor)                         Browser (staff)
  │  Blade + Tailwind + Alpine.js           │  Blade + Tailwind admin
  ▼                                         ▼
Laravel 12  (cbp-chat.weststar-dev.com)
  ├─ Auth: session guest (chat_users) + admin guard (users)
  ├─ OTP mail (SMTP mail.weststar-dev.com)
  ├─ AgentsApi service ───────────────► api.weststar-ai.com/v1/agents (Bearer key)
  ├─ Conversation + message log (MariaDB weststar_cbp-chat)
  └─ Quick-reply / canned-answer engine (local DB, answered without hitting the LLM)
```

- **Server-relay only:** the browser never talks to weststar-ai directly; the Bearer key never leaves the server.
- **Hybrid answering:** exact quick-reply button clicks are answered from the local `quick_replies` table (instant, deterministic — preserves the old bot's menu UX); free-text goes to the agent `/chat` endpoint with recent history (last ~10 messages).
- **Two chat surfaces:** full-page chat at `/chat`, plus an **embeddable widget** (`/widget` in an iframe + `public/embed.js` one-line snippet) matching the current CBP-website popup (min/expand header, form-first).

## 4. Database (MariaDB `weststar_cbp-chat`)

> Hyphenated DB name — Laravel quotes identifiers so it's fine; raw SQL must backtick it.

| Table | Columns (essentials) |
|---|---|
| `users` | Laravel default — **admin staff only** |
| `chat_users` | id, name, email (index), email_verified_at, last_seen_at, timestamps |
| `otp_codes` | id, chat_user_id FK, code_hash, expires_at, consumed_at, attempts, timestamps |
| `conversations` | id, chat_user_id FK, agent_id (remote), title (first msg excerpt), status, timestamps |
| `messages` | id, conversation_id FK, role (`user`/`assistant`/`system`), content (text), citations (json null), source (`agent`/`quick_reply`/`fallback`), latency_ms, timestamps |
| `quick_replies` | id, parent_id (self FK null ⇒ top menu), label, answer (text null — null ⇒ submenu only), sort_order, is_active, timestamps |
| `settings` | key (pk), value (text, encrypted cast for secrets) — `weststar_ai_key`, `agent_id`, `welcome_message`, `fallback_message`, `otp_ttl_min`, `widget_enabled` |
| + `sessions`, `cache`, `jobs` | Laravel infra (database drivers — shared-hosting friendly) |

Seeders: admin user (`admin@weststar-dev.com` / `cbpchat2026`), settings defaults, and the **old bot's menu tree** as `quick_replies` seed (Pembiayaan Peribadi/Korporat/Koperasi/Perumahan & Hartanah, Perkhidmataan Ar-Rahnu, Akaun Simpanan, Syer Anggota, Pelanggan, Lain-lain → submenus e.g. Permindahan IBG, Status Pembiayaan, Biro Angkasa, Dividen, MAPT…).

## 5. Modules & Routes

### M1 — Visitor registration + OTP
- `GET /` landing → register card (Chombee mascot, CBP logo, BM copy: *"Hai! Sila isi borang di bawah dan saya akan bantu anda."*) — fields **Nama\*** and **Alamat Emel\***, button **Mulakan Chat**.
- `POST /register` → upsert `chat_users` by email → generate 6-digit OTP (hashed, TTL 10 min, max 5 attempts, 60 s resend cooldown) → send mail → `GET /verify`.
- **OTP email (proper content):** branded Blade mailable — CBP logo header, BM primary + EN secondary, big 6-digit code, expiry note, "abaikan jika bukan anda", footer *Co-opbank Pertama Chat — automated message*. From `support@weststar-dev.com` as **"Co-opbank Pertama Chat"**.
- `POST /verify` (+ `POST /verify/resend`) → mark verified, log into `chat_user` session guard → `/chat`. Returning verified users within session TTL skip straight to chat.

### M2 — Chat UI (visitor)
- `GET /chat` — full-page: sticky CBP header, message list (Chombee avatar for bot, right-aligned user bubbles), quick-reply button stacks (indigo, like the old bot), typing indicator, input bar. Mobile-first; Tailwind palette from brand: indigo `#2E3192`-ish primary, CBP red/blue accents.
- `POST /chat/send` (AJAX/fetch, JSON): if payload matches an active quick reply → local answer (+ children as next button stack); else relay to Agents `/chat` with history → persist both sides in `messages`, return `{answer, citations, quick_replies?}`.
- Greeting on new conversation: *"Hai Salam! Saya Co-opbank Pertama Chat di sini untuk membantu anda."* + top-level quick-reply menu (parity with the old bot).
- `GET /widget` (iframe-safe, minimal chrome, min/expand controls) + `public/embed.js` snippet; `X-Frame-Options` relaxed for `/widget` only.

### M3 — `/admin`
Layout: Tailwind sidebar (CBP logo), auth via `users` guard, throttled login.

| Page | Function |
|---|---|
| Dashboard | totals: chat users, conversations today, messages, agent doc/chunk counts, API health ping |
| **Agent Profile** | view/edit remote agent: name, system prompt (textarea), model, temperature, top_k → `PATCH /v1/agents/{id}`; create/select agent if none configured |
| **RAG / Knowledge** | list documents (title, source, status, chunks) · add by URL · upload file (pdf/doc) · refresh status |
| **Agent Answers** | CRUD `quick_replies` menu tree (nested, drag/sort, activate) + edit `welcome_message` / `fallback_message` |
| Conversations | browse transcripts (user, time, source badges, citations), read-only |
| Chat Users | list/search registered visitors, verified state |
| Settings | Weststar AI key (masked), agent id picker (from `GET /v1/agents`), OTP TTL, widget on/off |

### M4 — Mail
Mailable `OtpMail` (+ Markdown/Blade HTML template with inline CBP branding). SMTP already in `.env`.

## 6. Design language

- Assets in repo root → move to `public/assets/`: `CBP-mascot.png` (Chombee), `cbp_logo_2024_min.png`, `logo CBP center.png`, `logo-chat.png`.
- Look: clean white cards, rounded-2xl, indigo primary buttons (old bot's button stacks modernised), light blue user-bubble tint, red accents sparingly (CBP logo colours), Poppins/Nunito-style rounded font. Widget matches the popup in the CBP site screenshot (light gray header, minimize + open-in-new icons).

## 7. Delivery phases

| Phase | Scope | Exit test |
|---|---|---|
| P0 Scaffold | Laravel 12 app in `app/` subdir, Tailwind vite build, `.htaccess` docroot reroute, DB migrations+seeds, deploy.sh + `/_provision` | `/` renders on live |
| P1 Auth flow | register → OTP mail → verify → session | real OTP email received, verify works on live |
| P2 Chat | chat UI, quick-reply engine, AgentsApi relay, conversation logging, fallback state | button + free-text chat round-trips on live |
| P3 Admin | all admin pages, settings (key/agent), RAG upload, profile PATCH | doc uploaded via admin appears in `GET /documents` |
| P4 Widget + polish | `/widget`, embed.js, error pages, mobile pass | widget iframe works from another origin |
| P5 Test & handover | end-to-end walkthrough with real key (once provided), README | full flow green |

## 8. Deployment (weststar shared-hosting standard)

- **Layout:** Laravel app in `app/` (full framework), docroot `.htaccess` rewrites into `app/public` (host-matched, same pattern as other weststar sites). No SSH — **lftp mirror -R** via `deploy.sh` (no `--delete`; use ephemeral self-deleting `_cleanup.php` for remote deletions).
- `vendor/` built locally (PHP 8.2-compatible deps — host CLI is 8.2) and shipped as `vendor.zip` + `_unzip.php` on first deploy; subsequent deploys mirror changed app files only.
- `/_provision?token=PROVISION_TOKEN` route: create/migrate/seed DB, storage:link, config/route/view cache rebuild. Trigger via `curl`, never manual browser steps.
- Env on server maintained separately; **never blind download-append-reupload `.env`** (verify anchor lines first).
- Verify each phase live with `curl` (and Playwright screenshot for UI phases).

**Credentials (from `.env`):** FTP `weststar` @ `103.191.76.66` → `/public_html/cbp-chat.weststar-dev.com` · DB `weststar_cbp-chat` / `weststar_admin` · SMTP `support@weststar-dev.com` · provision token `08cc490cdc3903b2add466f3`.

## 9. Security

- Bearer key server-side only, encrypted at rest in `settings`; masked in admin UI.
- OTP hashed (`Hash::make`), single-use, attempt-limited, throttle `/register` + `/verify` (5/min/IP).
- CSRF on all forms; chat endpoint session-guarded; admin guard separate from chat users.
- No secrets in git; `.env` stays untracked.

## 10. Open items

1. **`WESTSTAR_AI_KEY`** — not found locally; paste into `/admin → Settings` (or server `.env`) when available. Everything else ships without it.
2. Old CBP bot's full menu/answer content — seeded from the screenshot's visible tree; admin can complete the rest via Agent Answers CRUD.
3. Document-delete endpoint unconfirmed in API docs — UI degrades gracefully.
