jim800121chen fb7da5d180 chore(autoflow): migrate .autoflow/ 共享層文件至 docs/autoflow/
依 autoflow-agent workspace v2 設計把 PRD / 設計 / 架構 / 交付類
共享文件從個人層 .autoflow/(ignored)搬到 docs/autoflow/(進 git),
讓團隊可共享產品與架構文件,個人層只留 progress / review / testing 等
per-branch 筆記。

- 02-prd/        21 個檔(PRD、features、market-analysis 等)
- 03-design/     18 個檔(design-spec、wireframes、flows 等)
- 04-architecture/ 31 個檔(TDD、design-doc、ADR×14、API 規格等)
- 07-delivery/   3 個檔(project-summary、phase-0.6-handover、stage-deployment-setup)

合計 73 檔。原檔已從 .autoflow/ 移除(migration 工具執行 git mv,
但因 .autoflow/ 在 .gitignore 中、git 將此操作視為新增、無 rename history)。
2026-05-04 16:55:55 +08:00

337 lines
12 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.

# API — Conversion轉檔功能Phase 0.8
> **base URL**`https://stage-9527.innovedus.com:9527/`stage / `http://localhost:3721`dev
> **Auth**OIDC cookie session`visiona_session`),參見 `oidc-tdd.md`
> **同層**`api/api-spec.md`(總覽)、`conversion.md`(內部設計)、`adr/adr-014-conversion-integration.md`
> **角色**:給 visionA-frontend 實作時的 API 契約
---
## 通用約定
| 項目 | 值 |
|------|-----|
| 通用回應格式 | `{ "success": true, "data": {...} }` / `{ "success": false, "error": {code, message, details?} }` |
| Auth | 走 cookiefrontend 用 `credentials: "include"` |
| Request ID | header `X-Request-Id`visionA-backend 沒收到會自動產生) |
| Content-Type | 除 `init``multipart/form-data` 外,其他 JSON |
---
## 1. `POST /api/conversion/init`
啟動轉檔 job — 把 multipart body streaming proxy 到 converter。
### Request
```
POST /api/conversion/init HTTP/1.1
Cookie: visiona_session=...
Content-Type: multipart/form-data; boundary=----xyz
```
multipart fields**注意:不要帶 user_idbackend 會從 cookie 灌**
| Field | Type | 必填 | 說明 |
|-------|------|-----|------|
| `model` | file | ✓ | `.onnx` / `.tflite`,≤ 500MB |
| `ref_images[]` | file × N | — | 可 0100 張,每張 ≤ 10MB |
| `model_id` | text | ✓ | 165535使用者自訂編號converter 要求) |
| `version` | text | ✓ | 例 `v1.0.0` |
| `platform` | text | ✓ | `520` / `720` |
| `enable_evaluate` | text | — | `true`/`false`,預設 `false` |
| `enable_sim_fp` | text | — | 同上 |
| `enable_sim_fixed` | text | — | 同上 |
| `enable_sim_hw` | text | — | 同上 |
### Response 200
```json
{
"success": true,
"data": {
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "running",
"stage": "onnx",
"progress": 0,
"stage_progress": 0,
"created_at": "2026-04-30T12:00:00Z",
"expires_at": "2026-05-07T12:00:00Z"
}
}
```
> `expires_at` = `created_at + 7d`converter 7 天 GC 截止時間。frontend 用於顯示倒數與切「已過期」狀態。詳見 `conversion.md` §2.6.2。
### 錯誤
| HTTP | code | 來源 | 處理建議 |
|------|------|-----|---------|
| 400 | `validation_failed` | converter | 顯示 details.fields |
| 401 | `unauthorized` | visionA | redirect `/login` |
| 409 | `active_job_exists` | visionA pre-check / converter | 顯示「你已有進行中任務」+ details.job |
| 413 | `payload_too_large` | converter | 提示檔案大小限制 |
| 502 | `converter_unavailable` | visionA | 提示「轉檔服務暫時無法使用」+ 重試按鈕 |
| 503 | `idp_unavailable` / `service_busy` | visionA / converter | 提示稍後重試 |
---
## 2. `GET /api/conversion/{job_id}`
查 job 狀態。Frontend 用 polling建議間隔 2 秒。
### Response 200
```json
{
"success": true,
"data": {
"job_id": "550e8400-...",
"status": "running",
"stage": "bie",
"progress": 45,
"stage_progress": 60,
"created_at": "2026-04-30T12:00:00Z",
"updated_at": "2026-04-30T12:05:30Z",
"expires_at": "2026-05-07T12:00:00Z",
"source_filename": "yolov5s.onnx",
"target_chip": "720",
"error_code": null,
"error_message": null
}
}
```
`status` enum`created` / `running` / `completed` / `failed`
`stage` enum`onnx` / `bie` / `nef`
| 欄位 | 用途 |
|------|------|
| `expires_at` | `created_at + 7d`frontend 顯示倒數 |
| `source_filename` | 原始檔名(顯示用,例 wireframe success card 「yolov5s.onnx → yolov5s_kl720.nef」|
| `target_chip` | 從 init 時的 `platform` 欄回傳(`520` / `720` / `630` / `730` |
### 錯誤
| HTTP | code | 處理 |
|------|------|-----|
| 403 | `forbidden` | job 不屬於當前 user |
| 404 | `not_found` | job_id 不存在 / 已過期 |
| 502 | `converter_unavailable` | 持續失敗 → 提示重試 |
### Polling 建議
- Frontend 收到 `status=running` → 2s 後再 poll
- `status=completed` / `failed` → 停止 polling
- 連續 5 次 5xx → 停止 polling 並顯示錯誤
---
## 3. `POST /api/conversion/{job_id}/promote-to-models`
「加到模型庫」 — 完整流程promote → FAA pull → 寫進 visionA model store。
### Request
```json
POST /api/conversion/{job_id}/promote-to-models
Content-Type: application/json
{
"name": "yolov5s_kl720"
}
```
| Field | 必填 | 說明 |
|-------|-----|------|
| `name` | ✓ | 在 model 庫顯示的名字。Design Phase 0.8 wireframe §7.1 要求此欄位,預設 `{job.source_filename_stem}_{target_chip.lower()}` |
| `description` | — | Phase 0.8 不送,留 Phase 1— backend 接受但忽略Phase 1 開放 |
> **與 Design 對齊(議題 #4**Phase 0.8 wireframe §7.1 的 import Dialog **只有名稱欄位**不含描述backend Phase 0.8 也只用 `name``description` 雖在 schema 內但不顯示給使用者填寫。Phase 1 Design 開放描述欄位時 backend 已 ready無需改 API。
### Response 201
```json
{
"success": true,
"data": {
"model_id": "abc-123",
"source": "converted",
"source_job_id": "550e8400-...",
"name": "YOLOv5 Face KL520",
"target_chip": "kl520",
"file_size": 12345678,
"status": "ready",
"created_at": "2026-04-30T12:30:00Z"
}
}
```
> **格式註記**:這個 response 是既有 `internal/model.Model` schema沿用其 `target_chip` 用 `"kl520"` 小寫格式。
> 跟 §2 / §5 conversion job 的 `target_chip` 用 `"720"`converter `platform` enum**不同欄位、不同來源**
> - conversion job來自 converter scheduler 的 `platform` 欄位(`"520"` / `"630"` / `"720"` / `"730"`
> - model.target_chipvisionA 既有 model schema`"kl520"` / `"kl720"` / etc
>
> visionA-frontend 統一 normalize 成 UI 內部形式 `KL520` / `KL720` 顯示(見 `lib/api/conversion.ts` `normalizeTargetChip`)。
> Phase 1 評估是否值得在 backend 把兩邊統一(可能影響既有 model store 多處 caller動範圍大
### 錯誤
| HTTP | code | 處理 |
|------|------|-----|
| 403 | `forbidden` | 不是該 user 的 job |
| 404 | `not_found` | job_id 不存在 |
| 409 | `job_not_completed` | job 還沒 completed不能 promote |
| 502 | `converter_unavailable` | promote 失敗,可重試 |
| 502 | `faa_unavailable` | FAA pull 失敗,可重試 |
**冪等性**:對同一 `job_id` 重複呼叫;若已建過 model record回 200 + 既有 model 詳情(不重新建)。
---
## 4. `GET /api/conversion/{job_id}/download`
「下載」 — visionA-backend server-side HTTP 302 redirect 到 FAA delegated URL。**Token 永遠不過 frontend JS**。
仿 FAA TestSite `DownloadFileDirect``FileAccessAgent.TestSite/Controllers/HomeController.cs:255-282`pattern。
### Request
```
GET /api/conversion/{job_id}/download HTTP/1.1
Cookie: visiona_session=...
```
無 query string、無 body。
### Response 302成功
```
HTTP/1.1 302 Found
Location: http://192.168.0.130:5081/files/jobs/550e8400-.../result.nef?access_token=opaque-token-xxx
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Pragma: no-cache
```
browser 自動 follow Location直連 FAA 下載 NEF。
### Frontend 使用方式
```html
<!-- 推薦anchor tagbrowser 原生處理 -->
<a href={`/api/conversion/${jobId}/download`} download>下載</a>
```
或:
```ts
// 程式化觸發
window.location.href = `/api/conversion/${jobId}/download`;
```
Frontend **不需要也看不到** download URL / token / object_key — 全在 server-side + browser navigation 中流轉。
**為什麼不需要 FAA CORS**browser navigation request包含 `<a href>` click 與 `window.location.href`)不適用 CORSCORS 只管 JS 發起的 fetch / XHR。Server-side 302 redirect + 同源 endpoint 完全在 CORS 範圍外。
### 錯誤(不 redirect依 Accept header 回 JSON 或 HTML 錯誤頁)
| HTTP | code | 處理 |
|------|------|-----|
| 401 | `unauthorized` | 沒登入redirect /login前端攔截|
| 403 | `forbidden` | 不是該 user 的 job |
| 404 | `not_found` | job_id 不存在 / 已過期 |
| 409 | `job_not_completed` | job 還沒 completed不能下載 |
| 502 | `converter_unavailable` | promote 失敗(首次下載且尚未 promote 過時可能發生)|
| 502 | `mc_token_unavailable` / `download_token_failed` | MC 換 delegated token 失敗,提示重試 |
**錯誤回應格式**:依 `Accept` header
- `Accept: application/json``{success:false, error:{code, message}}`
- `Accept: text/html`(一般 anchor 觸發) → HTML 錯誤頁browser 直接顯示
**注意**
- 每次「下載」按鈕都直接打 `/download` endpoint不要前端 cache 任何中間狀態
- Token TTL 短5 分鐘預設),不過反正 frontend 也碰不到 token
- 不會與 `promote-to-models` 衝突;兩者內部都會 ensurePromoted冪等兩條路徑都拿同一個 target_object_key
---
## 5. `GET /api/conversion/active`
查當前 user 是否有 active job — 給 frontend 在跳出「上傳」UI 前 pre-check。
### Response 200有 active
```json
{
"success": true,
"data": {
"has_active": true,
"job": {
"job_id": "550e8400-...",
"status": "running",
"stage": "bie",
"progress": 45,
"created_at": "2026-04-30T12:00:00Z",
"expires_at": "2026-05-07T12:00:00Z",
"source_filename": "yolov5s.onnx",
"target_chip": "720"
}
}
}
```
> 此 endpoint 與 `GET /api/conversion/{job_id}` 回傳同一個 `Job` shapewireframe §3.3、flow-conversion.md §5.1 依賴此 shape 做「進入頁面就直接落 processing 畫面」的恢復邏輯。
**重啟恢復行為Phase 0.8 強化)**:當 visionA-backend 重啟導致 in-memory ownership 丟失時,此 endpoint 會 fallback 對 converter 查 `GET /api/v1/jobs?user_id=<sub>&status=in_progress` 並重建 ownershiplazy rebuild。對 frontend 完全透明(同樣 endpoint、同樣 response shape。詳見 `conversion.md` §2.6.1。
### Response 200無 active
```json
{
"success": true,
"data": {
"has_active": false,
"job": null
}
}
```
### 用法
Frontend 在「轉檔」入口的 `/conversion` 頁載入時打這個 endpoint
- `has_active=true` → 顯示「你目前有進行中的任務」+ 跳轉到該 job 的進度頁
- `has_active=false` → 顯示上傳表單
---
## 錯誤碼總覽
對齊 `conversion.md` §6。前端 i18n key 統一 `conversion.error.<short-name>`
| code | HTTP | i18n key | 預設訊息zh-TW |
|------|------|----------|------------------|
| `validation_failed` | 400 | `conversion.error.validation` | 上傳的內容不符合要求 |
| `unauthorized` | 401 | `common.error.unauthorized` | 請先登入 |
| `forbidden` | 403 | `conversion.error.forbidden` | 你無權存取此任務 |
| `not_found` | 404 | `conversion.error.not_found` | 任務不存在 |
| `active_job_exists` | 409 | `conversion.error.active_job` | 你目前已有進行中的轉檔任務 |
| `job_not_completed` | 409 | `conversion.error.not_completed` | 任務尚未完成(`promote-to-models``download` 共用) |
| `payload_too_large` | 413 | `conversion.error.too_large` | 檔案超過大小限制 |
| `converter_unavailable` | 502 | `conversion.error.converter_down` | 轉檔服務暫時無法使用 |
| `faa_unavailable` | 502 | `conversion.error.faa_down` | 檔案存取服務暫時無法使用 |
| `download_token_failed` | 502 | `conversion.error.token_failed` | 無法取得下載授權MC 4xx|
| `mc_token_unavailable` | 502 | `conversion.error.token_failed` | 無法取得下載授權MC 5xx / 持續失敗) |
| `idp_unavailable` | 503 | `conversion.error.idp_down` | 認證服務暫時無法使用 |
| `service_busy` | 503 | `conversion.error.busy` | 系統繁忙,請稍後再試 |
---
## 版本記錄
| 日期 | 版本 | 變更 |
|------|------|------|
| 2026-04-30 | 0.1 | 初稿Phase 0.8 MVP 範圍) |
| 2026-04-30 | 0.2 | §4 download endpoint 從 `POST /{job}/download-token`(回 JSON `{download_url, expires_at}`)改為 `GET /{job}/download`HTTP 302 redirect仿 FAA TestSite `DownloadFileDirect` patterntoken 不過 frontend JS、不需 FAA CORS`job_not_completed` HTTP code 從 400 改為 409 + 補 `mc_token_unavailable` |
| 2026-04-30 | 0.3 | Phase 0.8 三方交叉審閱回饋整合Job response shape 補 `expires_at` / `source_filename` / `target_chip`(議題 #7`/api/conversion/active` 行為文件化 lazy rebuild 機制(議題 #2 重啟恢復);`promote-to-models` request body 對齊 Design 單欄位(議題 #4`description` 留 Phase 1 |