106 lines
6.8 KiB
Markdown
106 lines
6.8 KiB
Markdown
# HANDOFF — Sales Trainer
|
||
|
||
> Another AI should be able to resume without chat history.
|
||
|
||
## Branch / repo / deploy
|
||
- Repo: `~/Gitea/Sales Trainer/` — **git repo, remote = Gitea**.
|
||
- Remote: `https://git.moreminimore.com/kunthawat/sales-trainer.git` (GITEA_TOKEN via credential
|
||
helper; never committed).
|
||
- **Live deploy:** `https://moreminimoreapps-saletrainer.ahkhwd.easypanel.host` — EasyPanel,
|
||
auto-redeploys from Gitea on push to `main` via webhook (≈3 min). Dockerfile ships prebuilt
|
||
`frontend/dist/` (no npm in image). LLM vars set in EasyPanel env.
|
||
|
||
## What this is
|
||
Corporate multi-user **sales-training simulator**: admins create persona groups from a
|
||
product/service/idea (โฟกัส "สินค้า/บริการ/ไอเดีย"), app auto-generates **15 customer personas**
|
||
(5/tier A/B/C) via LLM; trainees pick a **scenario (social / พบหน้า)**, chat 1:1 one-shot to close a
|
||
sale; a **per-turn + final judge LLM** evaluates feelings and scores/coaches. Vue SPA + Flask API +
|
||
filesystem JSON storage (no SQL). i18n TH/EN. No self-registration (admin provisions).
|
||
|
||
## Roles
|
||
- **super_admin** (bootstrap `admin`) — full recipe (secret persona fields) + tenant admin.
|
||
- **admin** — manages groups/users, sees personas with **secret fields stripped** (IP protection).
|
||
- **user** (trainee) — trains against personas, own board.
|
||
|
||
## Current state — COMPLETE core + hardened
|
||
All backend + frontend built. **11 test suites green** (mock LLM):
|
||
|
||
```bash
|
||
cd backend
|
||
uv run python scripts/test_m0.py # auth/roles/no-self-reg
|
||
uv run python scripts/test_m1.py # group create + role visibility
|
||
uv run python scripts/test_setup.py # first-time admin setup (email+password+ToS)
|
||
uv run python scripts/test_security.py # path traversal / IDOR / XSS
|
||
uv run python scripts/test_scenario.py # 2 scenarios (social/f2f_call), recontact default
|
||
uv run python scripts/test_e2e.py # full flow -> won via judge
|
||
uv run python scripts/test_ip_protection.py # secret fields hidden from admin
|
||
uv run python scripts/test_saas_tenant.py # tenant isolation + rate-limit + audit
|
||
uv run python scripts/test_user_journey.py # idea-flow end-to-end
|
||
uv run python scripts/test_variant.py # clone-persona-from-persona
|
||
uv run python scripts/test_resume_decision.py # resume + per-turn LLM decision
|
||
|
||
# run backend (serves SPA from frontend/dist)
|
||
cd backend && uv run python run.py # Flask :5001
|
||
```
|
||
|
||
## Key behaviors (implemented)
|
||
- **One-shot:** 1 persona = 1 chat per user; result final (won/lost). `SessionStore` enforces.
|
||
- **Resume:** unfinished session resumes on re-entry — **no** scenario re-pick (same session+scenario).
|
||
- **Win/loss = per-turn LLM judge** (`Simulator.evaluate_turn`): every customer reply is evaluated →
|
||
`{mood, decision(buy|walk|pending), score_delta, reason}`; session ends when decision = buy/walk.
|
||
**Not** fixed keywords.
|
||
- **2 scenarios only:** `social`, `f2f_call`. Unknown → social.
|
||
- **Recontact = persona trait** (not a scenario): chats normally, then at turn ≥ 2 a time-lapse
|
||
system note ("⏳ ผ่านไป 2-3 สัปดาห์…"), then re-engages warmer.
|
||
- **Persona variant:** `POST /api/groups/<gid>/personas/<pid>/variant` — new persona (new id) that
|
||
**locks** pains/objections/levers/tolerance/special/recontact/goal/budget/difficulty/tier/product
|
||
**but varies** identity (name/profession/age/location/background/personality/opener). Lets a
|
||
trainee re-practice the same challenge (one-shot is per-persona). UI button on finished personas.
|
||
- **Auto 15 personas** on create; no "เพิ่มเติม" button (TARGET=15, retry up to 3× + accept ≥ 8 so
|
||
real LLM under-count doesn't 500).
|
||
- **IP protection:** `SECRET_PERSONA_FIELDS` (pains, objections, negotiation_levers, opener,
|
||
tolerance, rootCause, resolutionConditions) stripped for `admin`; full only for `super_admin`.
|
||
- **SaaS Phase 1–3 done:** tenant isolation (`g.org_id` + `assert_tenant`), login/chat rate-limit,
|
||
audit log (`data/audit/audit.jsonl`), org plan/seats/active model + `PATCH /api/admin/orgs/<id>`,
|
||
ToS consent on setup, org-scoped signed expiring CSV export (5-min HMAC).
|
||
- **Reduce raw JSON in UI:** `list_groups` returns lightweight summary (persona_count, no full array);
|
||
persona detail rendered as readable form/cards (pain = line-by-line, not `[object Object]`).
|
||
|
||
## Credentials / data (testing)
|
||
- Bootstrap super-admin `admin` / `1234` → first login forces email + new password + ToS consent.
|
||
- Live test users: `testadmin` / `1234` (admin), `testuser` / `1234` (user).
|
||
- A test group "CRM ระบบจัดการลูกค้า" exists on live (user keeps it; will delete it themselves).
|
||
- **LLM key is a placeholder on local `.env`** (`replace_me`). Real analyze/chat needs a real
|
||
`LLM_API_KEY` (+ `LLM_PROVIDER`/`LLM_MODEL`/`LLM_BASE_URL`) in EasyPanel env then redeploy.
|
||
|
||
## Environment / gotchas
|
||
- **Python 3.11 only** (system default 3.14 incompatible). Use `backend/.venv` or
|
||
`cd backend && uv run python …`.
|
||
- **Tooling guard crash:** commands whose first token is `./.venv/bin/python` trip a lifecycle guard
|
||
→ always use `uv run python`. Prefix `PYTHONPATH=` when needed.
|
||
- **Frontend build:** `cd frontend && npm run build` (works even with allowScripts restrictions).
|
||
Commit `frontend/dist/` with `git add -f` (it's gitignored otherwise); the Dockerfile needs it.
|
||
- **Deploy pattern:** commit + push → webhook auto-deploys in ~3 min. Cannot run Docker locally
|
||
(no Docker on this Mac) → test with nginx/`python http.server` or `uv run python run.py`.
|
||
- **No remote push without asking** unless it's the established auto-deploy cadence.
|
||
- `search_files`/`read_file` sometimes misdetect files as binary (e.g. `users.py`, some `.vue`);
|
||
use `terminal` + `sed -n 'N,Mp' <file>` or `awk` for those.
|
||
|
||
## Wire-authorized / known-limits
|
||
- Chrome/Vivaldi drivable via computer-use; current model (DeepSeek V4 Flash) reads screenshots
|
||
imperfectly, and window-edge resize to a 320px viewport didn't land — **mobile visual QA at
|
||
320/500px still needs the user's eyes** (open live URL on phone, or DevTools device toolbar).
|
||
Responsive CSS (640px, single-column, `flex-wrap`, `.btn-back`) is present + deployed.
|
||
|
||
## Next actions / backlog (also docs/FUTURE_WORK.md)
|
||
- Real `/legal` page (setup links to it), billing/payments, per-tenant storage volume, compressed
|
||
persona recipe, export-token polish.
|
||
- Mobile visual polish per user feedback on real device.
|
||
- Deeper: letting admins bulk-import personas, analytics drill-down per persona variant.
|
||
|
||
## Related docs
|
||
- `docs/PLAN.md`, `docs/SAAS_PLAN.md`, `docs/FUTURE_WORK.md`.
|
||
- `docs/engineering-log.md` + `docs/engineering-log/2026-08-09-idea-flow-qa-deploy.md` (this session:
|
||
idea-flow UX, 2-scenario + recontact trait, live QA + auto-deploy, per-turn LLM judge, persona
|
||
variant, 15-persona auto-gen).
|