Read only the schedules whose results changed, using the engine's agent status

Results polled one drafts endpoint per schedule every minute, archived ones
included, so the Network tab filled with video-drafts reads. Each poll now
reads GET /agents/status once (with If-None-Match, mostly a 304) and reads a
schedule's drafts again only when its status changed, while a production plan
is at work, or after 30 minutes so signed video URLs stay valid. Without the
status read, every schedule is read as before. The poll is now every 30s.

Also documents how Express uses the engine API in docs/engine-api.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Iqbal
2026-11-03 12:34:08 +07:00
parent 46730279cb
commit eb194df8e0
5 changed files with 262 additions and 24 deletions

145
docs/engine-api.md Normal file
View File

@@ -0,0 +1,145 @@
# Alur API Express → Engine learning-lab
Express tidak punya backend sendiri. Semua logika AI (perencanaan, pembuatan materi, video, riset) ada di engine **learning-lab** (FastAPI, `/api/v1`). Express hanya membuat *agent* (jadwal) beserta konfigurasinya, lalu membaca dan meninjau hasilnya.
## Jalur request
| Lingkungan | Jalur |
|---|---|
| Production | `express.bawana.com/api/*` → redirect Netlify ([netlify.toml](../netlify.toml)) → `https://learningstudio.bawana.com/api/*` |
| Dev | Proxy Vite `/api` → `ENGINE_URL` (default `http://localhost:8000`, [vite.config.ts](../vite.config.ts)) |
- Prefix `/api/v1` ditambahkan di [src/api/client.ts](../src/api/client.ts). Setiap request membawa `Authorization: Bearer <Clerk session token>`.
- Request baca dan aksi diatur lajunya oleh `pace` di `client.ts`, supaya tidak terkena rate limit proxy (503/429).
- Endpoint agent ada di `learning-lab/backend/app/api/v1/endpoints/agents.py` (semua di bawah `/organizations/current/agents`). Endpoint produksi konten ada di `agent_content.py` (`/organizations/current/agents/{id}/content`).
Di bawah ini, `AGENTS` = `/organizations/current/agents`.
## Jadwal = agent
Setiap jadwal Express adalah satu agent engine. Namanya diawali prefix Express, lalu dibaca lewat `expressAgents()` di [schedules.ts](../src/express/schedules.ts).
### Membuat jadwal (`createSchedule`)
| Langkah | Endpoint |
|---|---|
| (khusus video) Ambil harga satu video sebagai budget run | `GET AGENTS/video-quote?scenes=…&duration_seconds=…` |
| Buat agent dengan konfigurasi dari `configFor()` | `POST AGENTS` |
| Pasang dokumen Brain (semua dokumen yang siap, atau hanya yang dipilih) | `PUT AGENTS/{id}/knowledge-sources` |
| Aktifkan (menyetujui aturan produksi; sebelum aktif tidak ada yang berjalan) | `POST AGENTS/{id}/activate` |
### Mengelola jadwal
| Aksi | Endpoint |
|---|---|
| Ubah brief (engine mengembalikan agent ke draft, jadi langsung diaktifkan ulang atau dipause) | `PATCH AGENTS/{id}`, lalu `POST AGENTS/{id}/activate` atau `/pause` |
| Pause / resume | `POST AGENTS/{id}/pause`, `POST AGENTS/{id}/activate` |
| Arsipkan | `POST AGENTS/{id}/archive` |
| Riwayat run | `GET AGENTS/{id}/runs` |
| Kesiapan knowledge | `GET AGENTS/{id}/knowledge-readiness` |
## 1. Konten materi (summary, quiz, flashcard, slide, audio, SCORM, …)
**Konfigurasi agent** (`configFor`):
```ts
enabled_tools: ['knowledge.retrieve', 'compose.graph.retrieve', 'compose.create_material']
material_outputs: outputsFor(kind)
content_goal: { enabled: true, weekly_target, execution_mode: 'automatic' }
```
Audio dan SCORM adalah jenis buatan Express sendiri, disusun dari output Compose. Audio adalah summary narasi; SCORM adalah pasangan summary dan quiz dari satu item rencana.
**Produksi** (`AGENTS/{id}/content`):
| Langkah | Endpoint |
|---|---|
| "Produce now", atau tick otomatis jam 09:00 WIB | `POST …/content/planning-quotes` `{ target }` |
| Konfirmasi rencana | `POST …/content/plans/{plan}/confirm-planning` `{ revision }` |
| Approve dan start | Dijalankan otomatis oleh tick engine (mode `automatic`) |
| Retry satu item (maksimal 3 percobaan) | `POST …/plans/{plan}/items/{item}/retry-quote`, lalu `POST …/plans/{plan}/start` |
| Status rencana dan periode minggu ini | `GET …/content` (di-cache 30 detik oleh `productionStatus`) |
**Di sisi engine** (`backend/app/services/`):
- `agent_content_planning.py`: model mengusulkan topik beserta bukti dari isi Brain.
- `agent_content_execution.py`: membuat quote dan memesan kredit untuk setiap item.
- `agent_materials.py`: menjalankan Compose (`compose_creation`) untuk membuat materinya.
**Hasil**: `material-drafts`.
## 2. Video
**Konfigurasi agent**:
```ts
enabled_tools: ['knowledge.retrieve', 'compose.graph.retrieve', 'studio.create_video']
video_options: { max_scenes, max_duration_seconds, art_style: 'realistic', language }
run_schedule: { cadence: 'weekly' | 'daily', hour: 9, minute: 0, weekday: 0 }
```
**Memicu run**: tick jadwal di engine, atau `runNow`, yaitu `POST AGENTS/{id}/runs` dengan header `Idempotency-Key`.
**Di sisi engine**, `agent_worker.py` menjalankan:
`agent_video_drafts.py` (membuat project Video Studio dan memesan kredit) → `agent_video_script.py` → `agent_video_storyboard.py` → `agent_video_clips.py` → `agent_video_render.py`
**Hasil**: `video-drafts`. Satu draft tidak bisa di-retry sendiri; yang dilakukan adalah run baru dari jadwalnya, dan tombol retry hanya muncul di draft terbaru.
## 3. Riset sumber web
**Konfigurasi agent**:
```ts
enabled_tools: ['knowledge.retrieve', 'compose.graph.retrieve', 'browse.search', 'library.add_url']
research_options: { max_results: 5 }
auto_approve_sources
```
**Memicu run**: jam 07:00 WIB, atau `POST AGENTS/{id}/runs`.
**Hasil**: `source-drafts`.
## Membaca dan meninjau hasil ([results.ts](../src/express/results.ts))
| Jenis | Daftar | Detail | Aksi |
|---|---|---|---|
| Materi | `GET AGENTS/{id}/material-drafts` | `GET AGENTS/{id}/material-drafts/{draft}` | `POST …/material-drafts/{draft}/decision` `{ expected_revision, decision }` |
| Video | `GET AGENTS/{id}/video-drafts` | (judul diambil dari daftar) | `POST …/video-drafts/{draft}/review` `{ decision }`, `POST …/video-drafts/{draft}/cancel` |
| Sumber | `GET AGENTS/{id}/source-drafts` | — | `POST …/source-drafts/{draft}/review` `{ expected_revision, decision }` |
Ekspor slide deck ke PPTX memakai `POST /slide-deck/{deckId}/export`.
### Pola polling
`listResults()` dipakai oleh halaman Results dan Production (query key `['results']`, `RESULTS_POLL` = setiap 30 detik):
- Setiap poll membaca `GET AGENTS/status?archived=true` satu kali, dengan `If-None-Match` (opsi `revalidate` di `client.ts`). Kalau tidak ada perubahan, engine menjawab 304.
- Dari respons itu dibuat tanda per agent. Endpoint per jadwal (`material-drafts`, `video-drafts`, `source-drafts`) hanya dibaca ulang untuk agent yang tandanya berubah.
- Pembacaan ulang tetap dipaksa untuk jadwal konten dengan plan aktif (topik plan belum punya draft), setelah 30 menit (URL video bertanda tangan berlaku 1 jam), dan setelah setiap mutation (`forgetReads`, [main.tsx](../src/main.tsx)).
- Kalau `/status` sibuk, tanda terakhir dipakai. Kalau endpoint tidak tersedia, semua jadwal dibaca seperti dulu; jadwal arsip tetap cukup dibaca sekali.
- Paling banyak 4 request per jadwal berjalan bersamaan (`PER_SCHEDULE_LIMIT`). Konten dibaca dulu, baru riset.
- Kalau engine membalas 503/429, request dicoba sekali lagi setelah 3 detik. Kalau tetap gagal, hasil poll sebelumnya yang dipakai.
- Di engine, `_video_draft_out` menjalankan sekitar 5 query untuk setiap draft (project, run step, dokumen, scene, kredit), dengan batas 100 draft per agent.
## Brain dan kredit ([src/api/engine.ts](../src/api/engine.ts))
| Aksi | Endpoint |
|---|---|
| Daftar / status / hapus / unduh dokumen | `GET /upload/list`, `GET /upload/{id}`, `DELETE /upload/{id}`, `GET /upload/{id}/file` |
| Upload | `POST /upload/` |
| Format yang didukung | `GET /upload/supported-formats` |
| Knowledge graph dokumen | `GET /upload/{id}/knowledge`, `POST /upload/{id}/knowledge/build` |
| Proses ulang | `POST /upload/{id}/reprocess` |
| Versi baru atau bukan | `POST /upload/{id}/versions/link` atau `/decline` |
| Saldo kredit | `GET /organizations/me` |
| Pemilik jadwal | `GET /auth/me`, `GET /organizations/current/dashboard/members` |
## Kode lama yang tidak dipakai
Fungsi berikut di `src/api/engine.ts` tidak dipanggil di mana pun di `src/`. Ini sisa alur versi awal, yang memakai satu agent tunggal bernama `Learning Lab Express` dan memanggil Compose langsung:
- `generateText`, `generateQuiz`: `POST /compose/sets`, `/generate/summary`, `/generate/quiz`
- `ensureAgent`, `attachSources`, `setSchedule`, `research`
`resetAgentCache` masih dipakai oleh `AuthGate.tsx`.