Files
sales-trainer/README.md
Macky 3c22d88bcd feat: demo SaaS + training flow security hardening (8/8 review gate passed)
- Demo accounts: super_admin-only provisioning into isolated DEMO_ORG_ID tenant,
  30-day UTC trial on first login, revocable, one-time credential delivery via
  optional SES/webhook (never persisted). Adds boto3 dependency.
- Analytics/report/export/privacy: shared bounded scan budget across users/groups/
  sessions, tenant-consistent session/user/group joins, scalar-only CSV export
  (no nested persisted-value stringification).
- Ownership/tenant isolation: canonical owner-tenant predicate for list/read/chat;
  client sees is_owned only, never owner_user_id.
- Lifecycle/races: status transition validation, analyzing is an in-progress gate
  (no duplicate reanalysis), structured-ready publication, stale-variant revalidation.
- Auth/setup/consent/JWT/OAuth/config: fail-closed consent, bounded JWT lifetime,
  provider-subject atomic OAuth identity, repeated-secret rejection, strict Persona
  trait validation.
- Chat/session/privacy: pre-seller opener redaction, corrupt-session recovery,
  role-aware completed-chat dashboard routing.
- Frontend: Training→product→personas→practice flow, demo/role/demo guards,
  is_owned-based ownership display, 320×568 and 500×768 responsive E2E.
- 8 independent exact-five-key review scopes passed; backend 509, frontend 26,
  production build 1775 modules, isolated E2E 15.
2026-08-25 06:39:06 +07:00

145 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🎯 Sales Trainer
A **corporate, multi-user sales-training simulator**. Admins upload/describe a product; the app
analyzes it and generates **15 realistic customer personas** (5 per buying-intent tier) with real,
varied pains. Trainees **chat one-on-one** with each persona to practice closing a sale — customers
negotiate, stall, and refuse unless their pain is genuinely resolved. Debrief reveals + coaches.
Built on patterns from the **CrowdSight / MiroFish** swarm engine and clean-room Hermes Brain & Tools plugin.
---
## Features
- **Login + roles** (no self-registration): `super_admin` / `admin` / `user` / `demo`.
- Admin builds & edits **persona groups** (product + 15 personas), hand-edits personas, sees analytics.
- User (trainee) **can't create** — only selects a group and practices; sees own results.
- **Input** via form (product / segment / description) **and/or file upload** (.pdf/.md/.txt).
Product data is used mainly to extract **pains**; personas are reusable across similar products.
- **15 personas** (5 × tier A/B/C):
- A = ready to buy · B = unsure · C = not interested but has pain (hardest).
- Varied demographics, income, occupation, lifestyle, personality — consistent with product.
- **Pain variety** (directly-solvable / partial / unrelated red-herring).
- **Negotiation levers** (price, freebies, delivery time, scope, payment).
- **Initiation mode**: customer opens OR seller must open the sale (outbound, e.g. insurance).
- **Channel**: Facebook / LINE.
- Special tier-C **"wrong_text"** persona (appears to buy, loses interest, but still has pain).
- **One-shot rule**: a persona is chatted **once per user** (final); shared across other users.
- **Chat realism**: all tiers can lose; everything negotiates; hidden internal signals + latent
fields (pain/income/personality/budget) revealed only after the result.
- **Debrief**: short summary + **coaching** (how to answer better on weak-score messages),
scored by a **separate judge LLM** (no speed factor).
- **Training loop**: win/lose board, **weak-area analysis**, and **user-generated personas**
(weak-area "lock" or manual form).
- **Admin analytics**: close rate, avg score, hardest personas.
- **EN + TH** UI.
### Demo SaaS
- Roles include `super_admin`, `admin`, `user`, and `demo`.
- `demo` accounts are provisioned only by `super_admin` through `POST /api/admin/demo-accounts`,
isolated in the dedicated `DEMO_ORG_ID` tenant, and can use only demo-only `ready` groups. Demo
users cannot create groups/personas or change group visibility.
- The first successful demo login starts exactly one **30-day UTC trial**. A `super_admin` can
revoke a demo account; the one-time temporary password cannot be retrieved later.
- Demo/SES/webhook environment variables are documented in `backend/.env.example`; SES and webhook
delivery are opt-in and require deployment-specific configuration.
---
## Quick start
### Local (dev)
```bash
# backend (Python 3.11)
cd backend
uv venv --python 3.11 .venv
uv pip install -r requirements.txt --python .venv/bin/python
cp .env.example .env # set LLM keys + JWT_SECRET + BOOTSTRAP_ADMIN_PASSWORD
uv run python run.py # Flask on :5001
# frontend (separate terminal)
cd frontend
npm install
npm run dev # Vite on :3000 -> proxies /api to :5001
```
The first run creates a default super-admin named **`admin`** using the
`BOOTSTRAP_ADMIN_PASSWORD` environment variable. The value is never printed or
hard-coded by the application. On first login you'll be **forced to set your
email + change the password** before using the app.
### Docker / EasyPanel
```bash
cp .env.example .env # set LLM_API_KEY + strong JWT_SECRET + bootstrap password
docker compose up -d # single container serving frontend + API on :5001
```
---
## LLM config
Any OpenAI-compatible endpoint (OpenAI, DeepSeek, or custom base URL):
```env
LLM_PROVIDER=deepseek # deepseek | openai | custom
LLM_BASE_URL= # optional override
LLM_MODEL_NAME=deepseek-chat # optional override
LLM_API_KEY=sk-...
```
---
## Architecture
```
frontend/ Vue 3 + Vite SPA (login, dashboard, group builder, personas, chat, debrief,
gen-persona, weak-areas, analytics). Built to dist/ and served by Flask.
backend/ Flask API (JWT auth, roles, groups, analyzer, persona generator, chat simulator,
judge, trainee loop, analytics). Filesystem JSON persistence (no external DB).
app/services/ analyzer · persona_generator · simulator (+ judge) · report · trainee · groups · sessions
docs/PLAN.md full design & decisions record
```
- **Storage**: `backend/data/` — JSON files per entity (users, orgs, groups, sessions, my_personas).
- **LLM calls**: analyzer (sales kit + pain-fit), persona generator, persona chat, judge.
---
## Tests
Run with the built-in deterministic **mock LLM** (no external key needed):
```bash
cd backend
uv run python scripts/test_m0.py # auth/roles/no-self-registration
uv run python scripts/test_m1.py # group create + failure handling + role visibility
uv run python scripts/test_routes.py # all API routes registered
uv run python scripts/test_e2e.py # full flow: analyze→personas→chat→debrief→one-shot→board→analytics
```
Real-model verification requires a live `LLM_API_KEY` in `.env`.
---
## Default accounts
| Role | Username | Password |
|------|----------|----------|
| super_admin | `admin` | `BOOTSTRAP_ADMIN_PASSWORD` (forced to set email + change on first login) |
Admins create additional users (users/login has no self-registration).
## ⚠️ Data persistence (IMPORTANT)
All data (users, passwords, groups, sessions) lives in `DATA_DIR` (default `/app/backend/data`).
**This directory MUST be a persistent volume in production.** If the container restarts/recreates
without a volume, all data is wiped — including the admin password you changed on first login, so
you will not be able to log back in with your new password.
- Docker run: `-v ./data:/app/backend/data`
- Docker Compose: already mounts `./data:/app/backend/data` (see `docker-compose.yml`)
- EasyPanel / other platforms: **mount a persistent volume to `/app/backend/data`** (or point
`DATA_DIR` at a mounted path), otherwise user data resets on every deploy.