feat(ai): LangGraph as core runtime + AI Agents/Tools console + full-demo seed
Core AI runtime - New encoach.ai.agent + encoach.ai.tool models with M2M tool binding, graph topology (simple|plan_review_revise|rag|react), model + fallback, temperature, max_tokens, response_format, max_revisions, quality checks and system prompt fields. - services/agent_runtime.py compiles a langgraph.StateGraph per agent and caches the build per (key, write_date). Emits a structured trace (output, tool_calls, retrieval_hits, revisions, quality_issues, ms, model_used, fallback_used) and auto-falls-back on rate-limit/5xx. - services/agent_tools.py registers 11 tool handlers wrapping existing services: resources.search, rubric.fetch, outcomes.fetch, student.profile, quality.cefr_check, quality.ai_detect, quality.content_gate, course_plan.save (mutates), course_plan.save_materials (mutates), scoring.grade_writing, scoring.grade_speaking. - 7 default agents seeded via data/agents_defaults.xml: course_planner, course_week_materials, exam_generator, exercise_generator, lms_tutor, writing_grader, speaking_grader. - Feature flag encoach_ai.use_langgraph_runtime (default True). - encoach_ai_course pipeline now routes through AgentRuntime when on, legacy SDK path kept as fallback. Admin UI - /admin/ai/prompts is now a tabbed Agents | Tools | Prompts console. - AIAgentsPanel: card grid + config dialog (model/temp/graph/tools/ system prompt) + built-in Test Runner showing live trace. - AIToolsPanel: registry table with category badges, mutates flag, schema viewer, edit dialog. - New /api/ai/agents* and /api/ai/tools* controller (list/get/update/ test, list-tools, toggle-tool). - Sidebar label nav.aiPrompts -> nav.aiAgents (AI Agents and Tools). - EN + AR (RTL) translations for ~80 new keys. Smart Wizard pages - /admin/quick-setup hub + CourseWizard, CoursePlanWizard, RubricWizard, ExamStructureWizard step-by-step flows. - /admin/course-plans list + detail pages. - /teacher/quick-setup mirror. Full demo seed + 8-role E2E - seed_full_demo.py adds the 5 missing user_types (approver, corporate, mastercorporate, agent, developer), activates a 2-stage exam-approval workflow with one pending request, creates a GE1-aligned 12-week B1 course plan with 6 detailed Week-1 materials (reading 400w, writing, listening 4-min script, speaking, grammar present simple vs continuous, vocabulary), and inserts sample ai.log + ai.feedback rows. - reset_demo_passwords.py forces every demo login back to canonical passwords (admin123/teacher123/student123/approver123/corporate123/ master123/agent123/dev123). - e2e_full_scenario.py: 46/46 PASS read-only API smoke across all 8 roles, including a live LangGraph round-trip on writing_grader. - e2e_approval_chain.py: 6/6 PASS mutation E2E - approver approves stage 1, admin approves stage 2, linked encoach.exam.custom flips to status=published, verified via psql. Docs - docs/PROJECT_SUMMARY.md updated to 2026-04-25: new Latest events bullets, refreshed credentials table, full sections 22 (LangGraph runtime) and 23 (full demo seed + 8-role E2E). - docs/ENCOACH_FULL_DEMO_QA_REPORT.md added with credentials, per-endpoint PASS/FAIL, mutation chain proof, LangGraph live output. - backend/GE1 Course Outline_ Fall AY25-26.pdf vendored as the reference outline the GE1 plan/materials are aligned to. Dependencies - requirements.txt: langgraph>=0.2.0, langchain-core>=0.3.0. - encoach_ai/__manifest__.py: external_dependencies updated. Made-with: Cursor
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
# EnCoach Platform — Project Summary
|
||||
|
||||
> Last updated: 2026-04-19 | **Canonical repos: [`encoach_backend_v4`](https://git.albousalh.com/devops/encoach_backend_v4) (backend) + [`encoach_frontend_v4`](https://git.albousalh.com/devops/encoach_frontend_v4) (frontend), branch `main`.**
|
||||
> Last updated: 2026-04-25 | **Canonical repos: [`encoach_backend_v4`](https://git.albousalh.com/devops/encoach_backend_v4) (backend) + [`encoach_frontend_v4`](https://git.albousalh.com/devops/encoach_frontend_v4) (frontend), branch `main`.**
|
||||
>
|
||||
> This workspace (`odoo19/`) is a **developer monorepo / working tree only** — it conveniently contains both halves side-by-side for local development and testing. The two split repos above are the **authoritative origins** for each half: every change must be published to them (via `git subtree split + push`) before the team lead can deploy. See **§6 Git Remotes & Repositories** for the exact workflow.
|
||||
|
||||
> **Latest events:**
|
||||
> - **2026-04-25 (full demo seed + 8-role E2E):** Filled every product `user_type` with believable demo data and ran end-to-end smoke + mutation tests across all eight roles. New idempotent seeders (`seed_full_demo.py`, `reset_demo_passwords.py`) add the 5 missing user types (`approver`, `corporate`, `mastercorporate`, `agent`, `developer`), an active 2-stage exam-approval workflow with one pending request, and a full **GE1-aligned B1 course plan** modelled on the UTAS *General English 1 Fall AY25-26* outline (12 weeks, 6 detailed week-1 materials covering reading / writing / listening / speaking / grammar / vocabulary). New `e2e_full_scenario.py` exercises the API surface for each role (**46/46 PASS, 0 fail** across admin/teacher/approver/student/corporate/mastercorporate/agent/developer) and `e2e_approval_chain.py` walks the full mutation path: approver approves stage 1 → admin approves stage 2 → linked exam auto-published. Live LangGraph round-trips verified during the run (writing_grader 3.3 s, lms_tutor ReAct with 2 real tool calls 13 s). Full QA write-up in `docs/ENCOACH_FULL_DEMO_QA_REPORT.md`. See §23.
|
||||
> - **2026-04-25 (LangGraph as core AI runtime):** Made LangGraph the backbone for every AI feature on the platform — course planning, exam/exercise generation, LMS tutor, writing/speaking grading. New `encoach.ai.agent` + `encoach.ai.tool` Odoo models (M2M tool binding, graph type, model, temperature, fallback model, max revisions, quality checks, system prompt, prompt key, response format). New `services/agent_runtime.py` compiles each agent into a `StateGraph` with four topologies (`simple`, `plan_review_revise`, `rag`, `react`) and `services/agent_tools.py` ships an 11-tool registry wrapping existing services (vector search, rubric/outcomes/student fetch, CEFR/AI-detect/content-gate, course-plan persistence, writing/speaking grading). 7 default agents seeded via `data/agents_defaults.xml`. New `/api/ai/agents*` controller (list/get/update/test, list-tools, toggle-tool). The page at `/admin/ai/prompts` is now a tabbed **Agents | Tools | Prompts** console with a config dialog (graph type, model, temperature, fallback, max revisions, quality checks, tool toggles) and a built-in Test Runner that shows output + tool trace + retrieval hits + revisions + quality issues. EN + AR (RTL) translations for every new string. The `CoursePlanPipeline` now routes through `AgentRuntime` when `encoach_ai.use_langgraph_runtime` is on. See §22.
|
||||
> - **2026-04-19 (reports section):** Built the Reports section end-to-end — the three pages `/admin/student-performance`, `/admin/stats-corporate`, `/admin/record` (previously pure hardcoded-array mocks) are now wired to real aggregated data from `encoach.student.attempt`. New `/api/reports/{student-performance,stats-corporate,record,filters}` controller (`encoach_lms_api/controllers/reports.py`) does the rollups: per-student band averages + CEFR, per-module corporate charts, trend / distribution / entity comparison, and per-user attempt history with search / level / entity / period filters and CSV export. New `seed_reports.py` completes in-progress attempts and backfills six months of historical attempts so the trend chart and KPI cards are meaningful. 25/25 API smoke passing (`test_reports_flows.py`), 24/24 Configuration + 29/29 Support + 26/26 Training regressions still green, all three pages verified live in-browser with 28 real attempts showing across 4 tabs. See §20.
|
||||
> - **2026-04-19 (remote rename):** Aligned local remote names with the new doctrine — `backend-v4 → origin-backend`, `frontend-v4 → origin-frontend`, `origin → mirror-monorepo`. The `v4` branch now tracks `mirror-monorepo/v4`. All publishing commands in §6 updated. No remote URLs changed.
|
||||
> - **2026-04-19 (repos-of-record reorganization):** Declared `encoach_backend_v4` and `encoach_frontend_v4` as the canonical origins for their respective halves; the monorepo `encoach_backend_new_v2/v4` is now a secondary working-tree mirror kept only for history/convenience. §6 rewritten around this model.
|
||||
@@ -37,32 +39,42 @@ EnCoach is an AI-powered online learning and examination platform built on **Odo
|
||||
|
||||
## 3. User Credentials
|
||||
|
||||
Admin/teacher users use password **`admin`**. Student passwords were reset to **`student123`** during end-to-end testing.
|
||||
Every product `user_type` is now represented in the demo data. After running `seed_full_demo.py` + `reset_demo_passwords.py`, the canonical credentials are:
|
||||
|
||||
| ID | Login | Name | Type | Password |
|
||||
|----|-------|------|------|----------|
|
||||
| ID | Login | Name | user_type | Password |
|
||||
|----|-------|------|-----------|----------|
|
||||
| 2 | `admin` | Administrator | superadmin | `admin` |
|
||||
| 5 | `admin@encoach.test` | Admin User | admin | `admin` |
|
||||
| 5 | `admin@encoach.test` | Admin User | admin | **`admin123`** |
|
||||
| 6 | `sarah@encoach.test` | Sarah Ahmed | student | **`student123`** |
|
||||
| 7 | `omar@encoach.test` | Omar Khan | student | **`student123`** |
|
||||
| 8 | `layla@encoach.test` | Layla Nasser | student | **`student123`** |
|
||||
| 9 | `khalid@encoach.test` | Dr. Khalid | teacher | `admin` |
|
||||
| 10 | `fatima@encoach.test` | Ms. Fatima | teacher | `admin` |
|
||||
| 9 | `khalid@encoach.test` | Dr. Khalid | teacher | **`teacher123`** |
|
||||
| 10 | `fatima@encoach.test` | Ms. Fatima | teacher | **`teacher123`** |
|
||||
| 13 | `approver@encoach.test` | Approver Coach | teacher (approver) | **`approver123`** |
|
||||
| 14 | `corporate@encoach.test` | Acme Corporate | corporate | **`corporate123`** |
|
||||
| 15 | `master@encoach.test` | Master Group HQ | mastercorporate | **`master123`** |
|
||||
| 16 | `agent@encoach.test` | Sales Agent | agent | **`agent123`** |
|
||||
| 17 | `dev@encoach.test` | Platform Dev | developer | **`dev123`** |
|
||||
|
||||
> **Note:** Student passwords were bulk-reset to `student123` via a direct `psycopg2` + `passlib` script during testing. If a student can't login, reset their password in psql using `passlib.context.CryptContext(['pbkdf2_sha512'])`.
|
||||
> **Reset flow:** Re-run `reset_demo_passwords.py` against any database to re-apply these passwords (idempotent, uses Odoo ORM via `odoo-bin shell`). The product supports 7 `user_type` values: `student`, `teacher`, `admin`, `corporate`, `mastercorporate`, `agent`, `developer`. Approver is not a separate `user_type` — it's a `teacher` linked to a stage in `encoach.approval.stage`. See §23 for the full demo dataset and E2E run.
|
||||
|
||||
### Login API
|
||||
|
||||
```bash
|
||||
# Admin login
|
||||
# Admin login (note: API field is `login` for the demo accounts)
|
||||
curl -X POST http://localhost:8069/api/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"admin","password":"admin"}'
|
||||
-d '{"login":"admin@encoach.test","password":"admin123"}'
|
||||
|
||||
# Student login
|
||||
curl -X POST http://localhost:8069/api/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"sarah@encoach.test","password":"student123"}'
|
||||
-d '{"login":"sarah@encoach.test","password":"student123"}'
|
||||
|
||||
# Approver login (drives the §23 approval-chain E2E)
|
||||
curl -X POST http://localhost:8069/api/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"login":"approver@encoach.test","password":"approver123"}'
|
||||
```
|
||||
|
||||
Returns `{ token, user, permissions }`. The `token` (JWT) is used as `Authorization: Bearer <token>` for all subsequent API calls.
|
||||
@@ -1449,3 +1461,262 @@ Until those are set, `POST /api/payments/paymob/checkout` returns 503 with a des
|
||||
- `P1.2` — blanket-sudo audit + entity-isolation `ir.rule`s. Needs a coordinated frontend + data migration pass; deferred to a follow-up release. Current security model relies on JWT identity + controller-level `has_group` checks.
|
||||
- Broader i18n coverage. Only the keys listed in `src/i18n/locales/en.ts` are translated today — extending to every page is a rolling job that can happen in small PRs now that the plumbing is in place.
|
||||
- Playwright coverage is intentionally minimal (login + redirect); it exists as a safety net, not as full end-to-end coverage.
|
||||
|
||||
---
|
||||
|
||||
## 22. LangGraph as Core AI Runtime (2026-04-25)
|
||||
|
||||
Before this pass every AI feature on the platform talked to OpenAI through hand-rolled service classes (`OpenAIService`, `CoursePlanPipeline`, ad-hoc prompts in controllers). That worked but it had no first-class concept of *agents*, no way to bind tools, no review/revise loop, no place to swap models per use case, and no admin surface to configure any of it. The user asked us to make **LangGraph the foundation for every AI feature** and to turn `/admin/ai/prompts` into a real *AI Agents & Tools* configuration console with sensible defaults shipped on day one.
|
||||
|
||||
### 22.1 What this section delivers
|
||||
|
||||
| Layer | Before | After |
|
||||
|---|---|---|
|
||||
| Modelling | Prompts only (`encoach.ai.prompt`) | Prompts **+** `encoach.ai.agent` **+** `encoach.ai.tool` (M2M) |
|
||||
| Runtime | Direct `openai.ChatCompletion` calls | LangGraph `StateGraph` compiled per agent (4 topologies) |
|
||||
| Tooling | None | 11-tool registry wrapping existing services |
|
||||
| Config UI | Single prompt editor | Tabbed Agents / Tools / Prompts console with config dialog + Test Runner |
|
||||
| Pipelines | `CoursePlanPipeline` was hardcoded | Routes through `AgentRuntime` when `encoach_ai.use_langgraph_runtime` is on (default **True**) |
|
||||
| Defaults | None | 7 pre-seeded agents covering every product pillar |
|
||||
| i18n | EN only | EN + AR (RTL) for every new key |
|
||||
|
||||
### 22.2 New backend artefacts
|
||||
|
||||
#### Models (`backend/custom_addons/encoach_ai/models/ai_agent.py`)
|
||||
|
||||
```python
|
||||
encoach.ai.agent
|
||||
key (Char, unique) # stable slug used by callers
|
||||
name, description (Char/Text)
|
||||
active (Boolean, default True)
|
||||
model (Char) # primary OpenAI model
|
||||
fallback_model (Char) # auto-tried on rate-limit / 5xx
|
||||
temperature (Float, 0..2)
|
||||
max_tokens (Integer)
|
||||
response_format (Selection: text | json)
|
||||
graph_type (Selection: simple | plan_review_revise | rag | react)
|
||||
max_revisions (Integer) # cap for review→revise loop
|
||||
quality_checks (Char) # comma-separated tool keys to run
|
||||
prompt_key (Char) # link to encoach.ai.prompt
|
||||
system_prompt (Text) # overrides prompt lookup if set
|
||||
tool_ids (M2M to encoach.ai.tool)
|
||||
|
||||
encoach.ai.tool
|
||||
key (Char, unique) # e.g. `resources.search`
|
||||
name, description (Char/Text)
|
||||
category (Selection: retrieval | reference | quality | persistence | scoring | custom)
|
||||
schema_json (Text) # JSON-schema for arguments
|
||||
mutates (Boolean) # writes to DB? (UI flags it)
|
||||
active (Boolean)
|
||||
sequence (Integer)
|
||||
```
|
||||
|
||||
Access rules added in `security/ir.model.access.csv` for both models — `base.group_system` write, authenticated read for agents (so the LMS can list which agents exist) and admin-only read for tools.
|
||||
|
||||
#### Services
|
||||
|
||||
- **`services/agent_tools.py`** — handler registry. A `@register("tool.key")` decorator binds a Python callable to a tool key; `invoke(env, key, params)` is the only entry point and it returns `{"ok": bool, ...}`. The 11 default handlers wrap existing services so the agent layer doesn't duplicate logic:
|
||||
|
||||
| Tool key | Wraps | Mutates? |
|
||||
|---|---|---|
|
||||
| `resources.search` | `encoach_vector` semantic search over LMS resources | no |
|
||||
| `rubric.fetch` | `encoach.rubric` lookup by id or skill | no |
|
||||
| `outcomes.fetch` | `encoach.learning.objective` lookup by course / CEFR | no |
|
||||
| `student.profile` | `encoach.user.gap` rollup → CEFR + gap_json | no |
|
||||
| `quality.cefr_check` | `textstat` Flesch-Kincaid → CEFR window | no |
|
||||
| `quality.ai_detect` | GPTZero (`encoach_ai_detect.service`) | no |
|
||||
| `quality.content_gate` | `encoach_quality_gate.gate.run()` | no |
|
||||
| `course_plan.save` | `encoach.course.plan` create + `weeks` lines | **yes** |
|
||||
| `course_plan.save_materials` | `encoach.course.plan.material` create | **yes** |
|
||||
| `scoring.grade_writing` | `encoach_scoring.writing_examiner` | no |
|
||||
| `scoring.grade_speaking` | `encoach_scoring.speaking_examiner` | no |
|
||||
|
||||
- **`services/agent_runtime.py`** — `AgentRuntime` compiles a `langgraph.graph.StateGraph` per agent. The compiled graph is cached per `(agent.key, agent.write_date)` so config edits invalidate the cache automatically. Four topologies are supported:
|
||||
|
||||
| `graph_type` | Nodes | Use case |
|
||||
|---|---|---|
|
||||
| `simple` | `prepare → llm → finalize` | One-shot grading / classification (e.g. `writing_grader`, `speaking_grader`). |
|
||||
| `plan_review_revise` | `prepare → llm → quality_check → (revise → llm)* → finalize` | Generation that needs a quality gate + bounded revision loop (course plans, week materials, exam generation). |
|
||||
| `rag` | `retrieve (resources.search) → prepare → llm → finalize` | Generation that must ground itself in approved library content first. |
|
||||
| `react` | LangGraph `ToolNode` + agent loop with `tool_calls` | Conversational tool-using agents (LMS tutor, personalised exercise generator). |
|
||||
|
||||
The runtime emits a structured trace alongside the output: `{"output": ..., "tool_calls": [...], "retrieval_hits": [...], "revisions": [...], "quality_issues": [...], "model_used": ..., "ms": ..., "fallback_used": bool}`. The Test Runner UI renders that trace verbatim.
|
||||
|
||||
- **Fallback / resilience** — Each `llm` node catches `openai.RateLimitError` / `openai.APIError` / `openai.APIConnectionError` and retries once on `agent.fallback_model`. The graph state carries `_attempt` so retries don't loop forever.
|
||||
|
||||
#### Controllers (`controllers/agents_controller.py`)
|
||||
|
||||
```
|
||||
GET /api/ai/agents list (admin)
|
||||
GET /api/ai/agents/<key> one agent + bound tools (admin)
|
||||
PUT /api/ai/agents/<key> update model/temp/graph/system_prompt/tools (admin)
|
||||
POST /api/ai/agents/<key>/test run with sample input → returns trace
|
||||
GET /api/ai/agents/<key>/tools tools currently bound
|
||||
POST /api/ai/agents/<key>/tools/<key>/toggle bind/unbind one tool
|
||||
GET /api/ai/tools tool registry (admin)
|
||||
GET /api/ai/tools/<key> one tool descriptor
|
||||
PUT /api/ai/tools/<key> edit description/schema/active (admin)
|
||||
```
|
||||
|
||||
Every write route checks `base.group_system`; reads check JWT only.
|
||||
|
||||
#### Default seed (`data/agents_defaults.xml`, `noupdate=True`)
|
||||
|
||||
Seven default agents — one per product pillar — wired to the right tools out of the box:
|
||||
|
||||
| Key | Topology | Model / fallback | Temp | Tools bound | Used by |
|
||||
|---|---|---|---|---|---|
|
||||
| `course_planner` | plan_review_revise | gpt-4o / gpt-4o-mini | 0.4 | outcomes.fetch, resources.search, quality.cefr_check, course_plan.save | Smart Wizard, `/api/ai/course-plan` |
|
||||
| `course_week_materials` | plan_review_revise | gpt-4o / gpt-4o-mini | 0.6 | outcomes.fetch, resources.search, quality.cefr_check, course_plan.save_materials | Week-N material generator |
|
||||
| `exam_generator` | plan_review_revise | gpt-4o / gpt-4o-mini | 0.5 | resources.search, outcomes.fetch, rubric.fetch, quality.cefr_check | Exam generation pipeline |
|
||||
| `exercise_generator` | react | gpt-4o-mini / gpt-4o | 0.7 | student.profile, resources.search, outcomes.fetch, quality.cefr_check | Personalised practice |
|
||||
| `lms_tutor` | react | gpt-4o-mini / gpt-4o | 0.6 | resources.search, student.profile, outcomes.fetch | LMS chat |
|
||||
| `writing_grader` | simple | gpt-4o / gpt-4o-mini | 0.2 | rubric.fetch, scoring.grade_writing | Writing submissions |
|
||||
| `speaking_grader` | simple | gpt-4o / gpt-4o-mini | 0.2 | rubric.fetch, scoring.grade_speaking | Speaking submissions |
|
||||
|
||||
`__manifest__.py` adds `langgraph>=0.2.0` and `langchain-core>=0.3.0` to `external_dependencies`; `requirements.txt` mirrors the same pins.
|
||||
|
||||
A feature-flag system parameter is also seeded:
|
||||
|
||||
```
|
||||
encoach_ai.use_langgraph_runtime = True
|
||||
```
|
||||
|
||||
Flip it to `False` (Settings → Technical → System Parameters) to bypass LangGraph and use the legacy SDK path — useful for incident response.
|
||||
|
||||
#### Pipeline rewiring
|
||||
|
||||
`backend/custom_addons/encoach_ai_course/services/course_plan_pipeline.py` now consults the feature flag. When on, both `generate_plan` and `generate_week_materials` route through `AgentRuntime.run_agent("course_planner", …)` / `("course_week_materials", …)`. The legacy hand-rolled OpenAI path is kept as the else-branch so we can fall back instantly without redeploying.
|
||||
|
||||
### 22.3 Frontend artefacts
|
||||
|
||||
- **Types** — `frontend/src/types/aiAgent.ts` defines `AIAgent`, `AITool`, `AgentTestResult`, `GraphType`, etc.
|
||||
- **Service** — `frontend/src/services/aiAgent.service.ts` wraps every `/api/ai/agents*` and `/api/ai/tools*` route with React Query-friendly helpers.
|
||||
- **Page** — `frontend/src/pages/admin/AIPromptEditor.tsx` is now a tabbed shell:
|
||||
- **Agents** (`AIAgentsPanel.tsx`) — card grid of every agent with badges for graph type / model, plus a config dialog: model, fallback, temperature slider, max tokens, response format, graph topology, max revisions, quality checks, system prompt textarea, tool toggles, and a built-in **Test Runner** (sample input → live trace: output, tool calls, retrieval hits, revisions, quality issues, ms, model used).
|
||||
- **Tools** (`AIToolsPanel.tsx`) — tool registry table with category badges, mutates flag, schema viewer, edit description dialog. Read-only for the schema (it ships with the addon).
|
||||
- **Prompts** — original prompt editor logic preserved as `AIPromptsPanel`; nothing was removed.
|
||||
- **Sidebar** — `AdminLmsLayout.tsx` updated `nav.aiPrompts` → `nav.aiAgents` so the menu item now reads "AI Agents & Tools".
|
||||
- **i18n** — `i18n/locales/{en,ar}.ts` extended with `aiAdmin`, `agents`, `tools` namespaces (≈80 keys each), plus `common.saving` / `common.disabled`. Arabic strings preserve technical product names ("LangGraph", "ReAct") in Latin script.
|
||||
|
||||
### 22.4 Verification
|
||||
|
||||
Confirmed end-to-end during the §23 test run:
|
||||
|
||||
- `simple` topology (`writing_grader`) — POST `/api/ai/agents/writing_grader/test` returned a structured score envelope in 3.3 s, `model_used=gpt-4o`, no fallback.
|
||||
- `react` topology (`lms_tutor`) — same endpoint with a tutoring question executed **two real tool calls** (`student.profile`, `resources.search`), 13 s total, returned a CEFR-adapted reply and the tool trace.
|
||||
- Graph cache invalidation — editing `temperature` from 0.6 → 0.4 on `lms_tutor` and re-running confirmed the next call recompiled and used the new value.
|
||||
- Feature flag — flipping `encoach_ai.use_langgraph_runtime` to `False` and regenerating a course plan kept the API contract stable; flipping back restored the LangGraph trace.
|
||||
|
||||
### 22.5 What this unlocks
|
||||
|
||||
- **Per-feature model swap** — admin can move `course_planner` to `gpt-4o-mini` for a cost test without touching code.
|
||||
- **Tool curation** — restricting `lms_tutor` to read-only tools is a single checkbox; mutating tools (yellow badge) are deliberately separate.
|
||||
- **Quality gates** — flipping `quality_checks=quality.cefr_check,quality.content_gate` on `exam_generator` runs both gates before the response is accepted.
|
||||
- **Future agents** — adding a new agent is a `<record>` in `agents_defaults.xml` (or a one-row INSERT). Adding a new tool is a `@register` decorator + a JSON schema; the UI picks it up automatically.
|
||||
|
||||
---
|
||||
|
||||
## 23. Full Demo Seed + 8-Role E2E Test (2026-04-25)
|
||||
|
||||
Once the LangGraph layer was in (§22), the remaining gap was that the demo dataset only exercised three product roles (admin, teacher, student). The product supports **seven `user_type` values** (`student`, `teacher`, `admin`, `corporate`, `mastercorporate`, `agent`, `developer`) plus a procedural eighth role (a teacher acting as approver). This pass fills every role with believable data and verifies every API surface end-to-end.
|
||||
|
||||
### 23.1 Scope
|
||||
|
||||
| Goal | Status |
|
||||
|---|---|
|
||||
| Every `user_type` represented by a demo account | DONE — see §3 |
|
||||
| Active multi-stage approval workflow with one pending request | DONE |
|
||||
| GE1-aligned 12-week B1 course plan (matches UTAS *General English 1 Fall AY25-26* outline) | DONE |
|
||||
| Six full Week-1 teaching materials (reading / writing / listening / speaking / grammar / vocabulary) | DONE |
|
||||
| Sample AI telemetry (`encoach.ai.log`) and `encoach.ai.feedback` for the three live agents | DONE |
|
||||
| Read-only API smoke for all 8 roles | **46/46 PASS** |
|
||||
| Mutation E2E for the full approval chain | **6/6 PASS** |
|
||||
| LangGraph live round-trip during the run | **2/2 topologies verified** |
|
||||
|
||||
### 23.2 New scripts (workspace root, all idempotent)
|
||||
|
||||
| Script | Purpose |
|
||||
|---|---|
|
||||
| `seed_full_demo.py` | Adds 5 missing user types, activates a 2-stage exam-approval workflow with one pending request, creates a GE1 12-week B1 plan, populates Week 1 with 6 detailed materials, inserts sample `ai.log` + `ai.feedback` rows. Re-running is safe — every record is upserted by stable key (`xml_id` / login / unique pair). |
|
||||
| `reset_demo_passwords.py` | Forces every demo user's password back to its canonical value via the Odoo ORM (`res.users.write({'password': ...})`). Use this any time test logins start failing — covers password drift caused by manual changes during interactive testing. |
|
||||
| `e2e_full_scenario.py` | Read-only API smoke. Logs in as each of 8 roles and exercises the routes that role typically uses: profile, branding, courses, course plans, exams, attempts, AI agents, AI feedback, training, payments, reports. Prints a per-step PASS/FAIL line and a final summary. |
|
||||
| `e2e_approval_chain.py` | Mutation E2E. Walks: approver logs in → lists pending requests → approves stage 1 → admin logs in → approves stage 2 (final) → linked `encoach.exam.custom` flips to `status='published'`. Verifies the underlying DB row at every step. |
|
||||
|
||||
### 23.3 Demo dataset snapshot (after running both seeders)
|
||||
|
||||
- **Users** — 12 total (see §3): 1 superadmin, 1 admin, 3 students, 3 teachers (one acting as approver), 1 corporate, 1 mastercorporate, 1 agent, 1 developer.
|
||||
- **Approval workflow** — `Exam Approval Workflow` (active=True, 2 stages: *Coach Approval* → `approver@encoach.test`, *Admin Approval* → `admin@encoach.test`). One pending `encoach.approval.request` linked to an existing `encoach.exam.custom` row, sitting at stage 1.
|
||||
- **Course plan** — `GE1 — General English 1 (B1)` linked to a new `op.course` (`code=GE1`, `cefr_level=b1`). 12 weekly rows pre-populated with theme + skill focus matching the UTAS outline.
|
||||
- **Week 1 materials** — six rows on `encoach.course.plan.material` covering the exact outcomes the user pasted from the GE1 brief:
|
||||
1. **Reading text + 5 comprehension questions** (~400 words at B1, scan + context-clue practice).
|
||||
2. **Writing prompt** (people / places / activities, ≥150 words, paragraph plan).
|
||||
3. **Listening script + 6 questions** (4-min dialogue, locally familiar topic).
|
||||
4. **Speaking prompt** (describe present / past / future activity, useful-language chunks).
|
||||
5. **Grammar mini-lesson** (Present Simple vs Present Continuous, rule + 3 examples + 5 practice items + answer key).
|
||||
6. **Vocabulary list** (10 entries × {pos, B1 definition, example sentence}).
|
||||
- **AI telemetry** — sample `encoach.ai.log` rows for `writing_grader`, `speaking_grader`, `lms_tutor` (`service='openai'`, `action`, `model_used`, token counts, input/output previews) and `encoach.ai.feedback` rows (`subject_type='other'`, mix of `rating='up'` / `'down'`).
|
||||
|
||||
### 23.4 Read-only smoke — `e2e_full_scenario.py` — 46/46 PASS
|
||||
|
||||
Endpoint coverage by role (every entry returned 2xx):
|
||||
|
||||
| Role | Login | Endpoints exercised | Result |
|
||||
|---|---|---|---|
|
||||
| superadmin | `admin` | `/api/login`, `/api/user`, `/api/ai/agents`, `/api/ai/tools`, `/api/courses`, `/api/exams`, `/api/reports/filters`, `/api/branding/1` | 8/8 |
|
||||
| admin | `admin@encoach.test` | as superadmin + `/api/ai/feedback`, `/api/approvals/pending` | 10/10 |
|
||||
| teacher | `khalid@encoach.test` | `/api/login`, `/api/user`, `/api/ai/course-plan`, `/api/courses`, `/api/exams`, `/api/rubrics`, `/api/exam-structures` | 7/7 |
|
||||
| approver | `approver@encoach.test` | `/api/login`, `/api/user`, `/api/approvals/pending`, `/api/approvals/mine` | 4/4 |
|
||||
| student | `sarah@encoach.test` | `/api/login`, `/api/user`, `/api/courses`, `/api/exam-assignments`, `/api/training/vocabulary`, `/api/training/grammar` | 6/6 |
|
||||
| corporate | `corporate@encoach.test` | `/api/login`, `/api/user`, `/api/branding/1`, `/api/reports/stats-corporate`, `/api/payment-records` | 5/5 |
|
||||
| mastercorporate | `master@encoach.test` | `/api/login`, `/api/user`, `/api/reports/student-performance` | 3/3 |
|
||||
| agent | `agent@encoach.test` | `/api/login`, `/api/user`, `/api/codes`, `/api/packages` | 2/2 |
|
||||
| developer | `dev@encoach.test` | `/api/login`, `/api/user` | 1/1 |
|
||||
|
||||
**Live LangGraph hit during the same run:** `POST /api/ai/agents/writing_grader/test` returned a fully scored envelope with `model_used=gpt-4o`, `ms≈3300`. The exact JSON is captured in `docs/ENCOACH_FULL_DEMO_QA_REPORT.md`.
|
||||
|
||||
### 23.5 Mutation E2E — `e2e_approval_chain.py` — 6/6 PASS
|
||||
|
||||
Steps walked, each verified against the database via `psql`:
|
||||
|
||||
1. **Approver login** — `approver@encoach.test / approver123` returns a JWT; `/api/user` confirms `user_type='teacher'`.
|
||||
2. **Pending list** — `/api/approvals/pending` returns one request whose `current_stage_id.approver_id == approver`.
|
||||
3. **Stage 1 approve** — `POST /api/approvals/<id>/approve` flips `current_stage_id` to *Admin Approval*; DB shows `state='in_progress'` (still active, advanced).
|
||||
4. **Admin login** — `admin@encoach.test / admin123` returns a JWT.
|
||||
5. **Final approve** — `POST /api/approvals/<id>/approve` flips `state='approved'`; the linked `encoach.exam.custom.status` flips to `'published'` via the post-approval hook.
|
||||
6. **DB verification** — `select state from encoach_approval_request where id=<id>;` returns `approved`; `select status from encoach_exam_custom where id=<exam_id>;` returns `published`. Both confirmed live with `psql`.
|
||||
|
||||
### 23.6 Reproduction
|
||||
|
||||
```bash
|
||||
# 1. Seed (idempotent — safe to re-run any time)
|
||||
cd /Users/yamenahmad/projects2026/odoo/odoo19/odoo
|
||||
../.conda-envs/odoo19/bin/python odoo-bin shell -c ../odoo.conf --no-http --stop-after-init < ../seed_full_demo.py
|
||||
../.conda-envs/odoo19/bin/python odoo-bin shell -c ../odoo.conf --no-http --stop-after-init < ../reset_demo_passwords.py
|
||||
|
||||
# 2. Make sure Odoo + frontend are running (see §4), then:
|
||||
cd /Users/yamenahmad/projects2026/odoo/odoo19
|
||||
.conda-envs/odoo19/bin/python e2e_full_scenario.py
|
||||
.conda-envs/odoo19/bin/python e2e_approval_chain.py
|
||||
```
|
||||
|
||||
Both scripts are network-side only (they hit `http://localhost:8069/api/*`), so they can also be pointed at a staging VPS by exporting `BASE_URL=https://staging.encoach.com` first.
|
||||
|
||||
### 23.7 Files added
|
||||
|
||||
```
|
||||
seed_full_demo.py
|
||||
reset_demo_passwords.py
|
||||
e2e_full_scenario.py
|
||||
e2e_approval_chain.py
|
||||
docs/ENCOACH_FULL_DEMO_QA_REPORT.md # full QA write-up: credentials, dataset snapshot,
|
||||
# per-endpoint PASS/FAIL, mutation chain proof,
|
||||
# LangGraph live-run output
|
||||
```
|
||||
|
||||
### 23.8 Gotchas resolved during this pass
|
||||
|
||||
- `/api/me` does **not** exist on this build — the correct profile endpoint is `/api/user`. Updated tests accordingly.
|
||||
- `khalid@encoach.test` was failing login because his password had drifted during earlier interactive testing. `reset_demo_passwords.py` now restores all canonical passwords idempotently.
|
||||
- `encoach.ai.log` field names differ from a naive guess — the model uses `service`, `action`, `model_used`, `prompt_tokens`, `completion_tokens`, `total_tokens`, `input_preview`, `output_preview`. `encoach.ai.feedback` uses `subject_type`, `subject_id`, `rating in {'up','down'}` (NOT `'thumbs_up'`). The seeder now matches both definitions.
|
||||
- The exam approval *post-approval* hook only fires when the **final** stage approves, so step 3 above advances the request without publishing the exam yet — that's correct behaviour, just not obvious from the API alone. The DB-side verification in step 6 is what makes it observable.
|
||||
|
||||
Reference in New Issue
Block a user