Add async AI generation tasks
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
# AI Generation Task Center Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Convert long-running DeepSeek generation requests into trackable asynchronous tasks with generation records, basic quota accounting, cost usage, and retry support.
|
||||
|
||||
**Architecture:** Keep the existing synchronous `IAiGenerateService` methods as the actual generation engine and add a DB-backed task layer in front of them. A task service creates task rows, reserves quota, and a worker claims pending tasks with a short database lock before executing generation outside the claim transaction. The Vue workbench starts tasks, polls status, and applies the generated result after success.
|
||||
|
||||
**Tech Stack:** Spring Boot, MyBatis XML mappers, MySQL tables in `sql/front_workbench.sql` and `sql/db.sql`, existing RuoYi thread pool conventions, Vue 3 + Element Plus.
|
||||
|
||||
---
|
||||
|
||||
### File Structure
|
||||
|
||||
- Create `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/AiGenerationTask.java`: task queue domain object.
|
||||
- Create `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/AiUsageLedger.java`: usage/cost ledger domain object.
|
||||
- Create `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/AiQuotaBucket.java`: per-user quota bucket domain object.
|
||||
- Create DTOs under `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto`: `AiGenerationTaskCreateRequest`, `AiGenerationTaskStatusResponse`, `AiQuotaResponse`.
|
||||
- Create mapper interfaces and XML files under `ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/front` and `ruoyi-generator/src/main/resources/mapper/front`.
|
||||
- Create services under `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front`: `IAiGenerationTaskService`, `AiGenerationTaskServiceImpl`, `AiGenerationTaskWorker`, `AiQuotaService`, `AiCostCalculator`, `AiTaskRetryPolicy`.
|
||||
- Modify `ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java`: add task create/status/retry/cancel/quota/history endpoints.
|
||||
- Modify `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiGenerateServiceImpl.java`: expose execution helpers or keep using the public sync methods from worker.
|
||||
- Modify `easycode-web/src/api/project.js`: add task APIs.
|
||||
- Modify `easycode-web/src/views/GenerateView.vue`: replace blocking generation calls with task start + polling.
|
||||
- Modify SQL scripts: add task, quota, and ledger tables.
|
||||
- Add focused tests in `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front`.
|
||||
|
||||
### Task 1: Task Status and Retry Policy
|
||||
|
||||
**Files:**
|
||||
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiTaskRetryPolicy.java`
|
||||
- Test: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/AiTaskRetryPolicyTest.java`
|
||||
|
||||
- [ ] **Step 1: Write failing tests**
|
||||
|
||||
Test retryable messages: timeout, 429, 5xx, and DeepSeek unavailable retry. Test validation/permission/quota messages do not retry. Test backoff sequence returns 30 seconds, 2 minutes, 5 minutes.
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `mvn -pl ruoyi-generator -Dtest=AiTaskRetryPolicyTest test`
|
||||
Expected: compile failure because `AiTaskRetryPolicy` does not exist.
|
||||
|
||||
- [ ] **Step 3: Implement minimal retry policy**
|
||||
|
||||
Create constants for max attempts and retry delays. Add `boolean canRetry(String errorMessage, int attempts, int maxAttempts)` and `Date nextRetryTime(int attempts, Date now)`.
|
||||
|
||||
- [ ] **Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `mvn -pl ruoyi-generator -Dtest=AiTaskRetryPolicyTest test`
|
||||
Expected: PASS.
|
||||
|
||||
### Task 2: Cost and Quota Services
|
||||
|
||||
**Files:**
|
||||
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiCostCalculator.java`
|
||||
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiQuotaService.java`
|
||||
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/AiQuotaBucket.java`
|
||||
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/AiUsageLedger.java`
|
||||
- Test: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/AiCostCalculatorTest.java`
|
||||
- Test: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/AiQuotaServiceTest.java`
|
||||
|
||||
- [ ] **Step 1: Write failing tests**
|
||||
|
||||
Test default quota allows a first task, blocks when daily count reaches 20, blocks when monthly cost reaches 5000 cents, reserves one running task, and releases running count on finish.
|
||||
|
||||
- [ ] **Step 2: Run tests to verify they fail**
|
||||
|
||||
Run: `mvn -pl ruoyi-generator -Dtest=AiCostCalculatorTest,AiQuotaServiceTest test`
|
||||
Expected: compile failure for missing services/domain types.
|
||||
|
||||
- [ ] **Step 3: Implement minimal services**
|
||||
|
||||
Use mapper methods for load/create/update quota rows. Cost calculator returns 0 when tokens are missing and uses configurable defaults for DeepSeek estimates.
|
||||
|
||||
- [ ] **Step 4: Run tests to verify they pass**
|
||||
|
||||
Run: `mvn -pl ruoyi-generator -Dtest=AiCostCalculatorTest,AiQuotaServiceTest test`
|
||||
Expected: PASS.
|
||||
|
||||
### Task 3: Task Persistence
|
||||
|
||||
**Files:**
|
||||
- Create: `AiGenerationTask` domain and mapper XML/interface.
|
||||
- Create: `AiUsageLedgerMapper` and `AiQuotaBucketMapper`.
|
||||
- Modify: `sql/front_workbench.sql`
|
||||
- Modify: `sql/db.sql`
|
||||
|
||||
- [ ] **Step 1: Write mapper/service tests**
|
||||
|
||||
Add tests for creating task rows, selecting task by user/project/task, selecting claimable tasks, updating lock/status, and inserting usage ledger.
|
||||
|
||||
- [ ] **Step 2: Run tests to verify they fail**
|
||||
|
||||
Run: `mvn -pl ruoyi-generator -Dtest=AiGenerationTaskServiceImplTest test`
|
||||
Expected: compile failure for missing domain/mapper/service methods.
|
||||
|
||||
- [ ] **Step 3: Implement MyBatis domain and XML**
|
||||
|
||||
Use status values `QUEUED`, `RUNNING`, `RETRY_WAITING`, `SUCCEEDED`, `FAILED`, `CANCELED`. Add indexes for `(status, next_retry_time)`, `(user_id, project_id)`, and `generation_id`.
|
||||
|
||||
- [ ] **Step 4: Run tests to verify they pass**
|
||||
|
||||
Run: `mvn -pl ruoyi-generator -Dtest=AiGenerationTaskServiceImplTest test`
|
||||
Expected: PASS.
|
||||
|
||||
### Task 4: Task Service and Worker
|
||||
|
||||
**Files:**
|
||||
- Create: `IAiGenerationTaskService`
|
||||
- Create: `AiGenerationTaskServiceImpl`
|
||||
- Create: `AiGenerationTaskWorker`
|
||||
- Modify: `FrontProjectController`
|
||||
- Test: `AiGenerationTaskServiceImplTest`
|
||||
|
||||
- [ ] **Step 1: Write failing service tests**
|
||||
|
||||
Test task creation validates project ownership, reserves quota, creates a `front_project_generation` row, creates a queued task, returns status DTO, and deduplicates a still-running same request hash.
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `mvn -pl ruoyi-generator -Dtest=AiGenerationTaskServiceImplTest test`
|
||||
Expected: compile failure or assertion failure before implementation.
|
||||
|
||||
- [ ] **Step 3: Implement task creation and status queries**
|
||||
|
||||
Generate request hash from `generateType + normalized request JSON`. Store request payload as JSON. Return `taskId`, `status`, `currentStep`, `progress`, `generationId`.
|
||||
|
||||
- [ ] **Step 4: Write failing worker tests**
|
||||
|
||||
Test worker executes app blueprint, database, and business blueprint tasks by delegating to existing sync service methods, marks success, records elapsed time, inserts ledger, and schedules retry for retryable failures.
|
||||
|
||||
- [ ] **Step 5: Implement worker**
|
||||
|
||||
Claim tasks using `locked_by` and `locked_until`, run outside the claim transaction, then update status. Keep max attempts at 3.
|
||||
|
||||
- [ ] **Step 6: Run tests to verify they pass**
|
||||
|
||||
Run: `mvn -pl ruoyi-generator -Dtest=AiGenerationTaskServiceImplTest,AiGenerationTaskWorkerTest test`
|
||||
Expected: PASS.
|
||||
|
||||
### Task 5: REST API
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java`
|
||||
- Test: service tests remain the main safety net.
|
||||
|
||||
- [ ] **Step 1: Add controller endpoints**
|
||||
|
||||
Add `POST /front/project/{projectId}/ai-tasks`, `GET /front/project/{projectId}/ai-tasks/{taskId}`, `POST /front/project/{projectId}/ai-tasks/{taskId}/retry`, `POST /front/project/{projectId}/ai-tasks/{taskId}/cancel`, `GET /front/project/{projectId}/generations`, and `GET /front/project/ai-quota`.
|
||||
|
||||
- [ ] **Step 2: Run compile tests**
|
||||
|
||||
Run: `mvn -pl ruoyi-admin -DskipTests compile`
|
||||
Expected: SUCCESS.
|
||||
|
||||
### Task 6: Frontend Polling Experience
|
||||
|
||||
**Files:**
|
||||
- Modify: `easycode-web/src/api/project.js`
|
||||
- Modify: `easycode-web/src/views/GenerateView.vue`
|
||||
|
||||
- [ ] **Step 1: Add task API helpers**
|
||||
|
||||
Add `createAiTask`, `getAiTask`, `retryAiTask`, `cancelAiTask`, `getAiQuota`, and `listGenerations`.
|
||||
|
||||
- [ ] **Step 2: Update generation handlers**
|
||||
|
||||
Replace direct calls to `generateAppBlueprint`, `generateDatabase`, and `generateBusinessBlueprint` with `createAiTask` plus polling. Keep the old direct API helpers for compatibility.
|
||||
|
||||
- [ ] **Step 3: Show task state**
|
||||
|
||||
Display status, progress, error message, retry button, and quota summary in the existing generate page.
|
||||
|
||||
- [ ] **Step 4: Run frontend tests/build**
|
||||
|
||||
Run direct `.mjs` tests with Node 20 and run Vite build.
|
||||
Expected: tests and build pass.
|
||||
|
||||
### Task 7: Final Verification
|
||||
|
||||
**Files:**
|
||||
- No new files.
|
||||
|
||||
- [ ] **Step 1: Run backend tests**
|
||||
|
||||
Run: `mvn test`
|
||||
Expected: SUCCESS.
|
||||
|
||||
- [ ] **Step 2: Run frontend tests/build**
|
||||
|
||||
Run easycode-web direct `.mjs` tests and Vite build.
|
||||
Expected: SUCCESS.
|
||||
|
||||
- [ ] **Step 3: Inspect git diff**
|
||||
|
||||
Run: `git diff --stat` and `git status --short`.
|
||||
Expected: only intended AI task center files changed.
|
||||
Reference in New Issue
Block a user