visionA/docs/autoflow/04-architecture/api/api-converter-contract.md
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

263 lines
7.4 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.

# Converter Integration Contract
> 本文件定義 **visionA-backend 呼叫 kneron_model_converter 的 API 契約**。
> 目的:提前把介面定義清楚,讓 converter 團隊知道要實作什麼;同時讓 visionA-backend 雛形可先用 stub 開發。
---
## 1. 通訊方向
```
visionA-backend/api-server kneron_model_converter
│ │
│ 1. POST /v1/jobs提交轉檔
│ ────────────────────────────────────►│
│ │
│ ◄────── 202 + {job_id} ──────────────│
│ │
│ 2. GET /v1/jobs/{id}(輪詢 / 或等 webhook
│ ────────────────────────────────────►│
│ ◄────── 200 + {status, result_url}──│
│ │
│ 3. 下載產物GET result_url
│ ────────────────────────────────────►│
```
Converter 可選:
- **Pull 模式**visionA 輪詢 `/v1/jobs/{id}`
- **Push 模式**visionA 提供 webhook URLconverter 完成後回呼
雛形先用 **Pull 模式**Phase 1 評估 webhook。
---
## 2. 認證
**visionA → converter**
- 服務對服務,使用 API Key 或 mTLS
- Header`Authorization: Bearer <VISIONA_CONVERTER_API_KEY>`
API Key 由 converter 團隊簽發,放 `VISIONA_CONVERTER_API_KEY` env。
---
## 3. 端點
### 3.1 POST `/v1/jobs` — 提交轉檔
**Request**
```json
POST /v1/jobs
Authorization: Bearer ...
Content-Type: application/json
{
"source": {
"type": "url",
"url": "https://storage.visiona.cloud/converter/source/demo-user/job-xxx.onnx?signature=...",
"checksum_sha256": "abc123...",
"format": "onnx"
},
"target": {
"chip": "kl520",
"quantization": "int8",
"input_shape": [1, 3, 224, 224]
},
"callback": {
"webhook_url": null, // 雛形先 null
"idempotency_key": "<uuid>"
},
"client_job_id": "<visionA 這邊的 job id對應 converter_jobs.id>"
}
```
**欄位說明**
| 欄位 | 必要 | 說明 |
|------|-----|------|
| `source.type` | ✓ | `"url"` \| `"upload"`(雛形只支援 url |
| `source.url` | ✓ | Presigned GET URLconverter 自己下載 |
| `source.checksum_sha256` | ✓ | visionA 計算好的 sha256converter 下載後驗證 |
| `source.format` | ✓ | `"onnx"` \| `"keras"` \| `"tflite"` \| ... |
| `target.chip` | ✓ | `"kl520"` \| `"kl720"` |
| `target.quantization` | — | `"int8"` \| `"fp16"`,預設依 chip |
| `target.input_shape` | — | 若 source 不含 shape由此補 |
| `callback.webhook_url` | — | 未來 push 模式 |
| `callback.idempotency_key` | ✓ | 重試時避免重複執行 |
| `client_job_id` | ✓ | visionA 內部 job idconverter 回傳時要帶上 |
**Response 202**
```json
{
"success": true,
"data": {
"job_id": "cvt-abc-123", // converter 側的 id
"status": "queued",
"accepted_at": "2026-04-21T12:00:00Z",
"estimated_duration_seconds": 120
}
}
```
**Response 錯誤**
```json
{
"success": false,
"error": {
"code": "UNSUPPORTED_FORMAT" | "INVALID_CHECKSUM" | "SOURCE_UNREACHABLE" | "QUOTA_EXCEEDED",
"message": "..."
}
}
```
---
### 3.2 GET `/v1/jobs/{job_id}` — 查詢狀態
**Response 200**
```json
{
"success": true,
"data": {
"job_id": "cvt-abc-123",
"client_job_id": "<visionA 的>",
"status": "queued" | "running" | "succeeded" | "failed",
"progress": 0.65, // 0.0 - 1.0
"stage": "quantizing", // 可選
"accepted_at": "...",
"started_at": "...",
"completed_at": "...",
"result": { // status == succeeded 時才有
"url": "https://converter.cloud/result/....nef?signature=...",
"url_expires_at": "...",
"checksum_sha256": "...",
"size_bytes": 12345678,
"target_chip": "kl520"
},
"error": { // status == failed 時才有
"code": "QUANTIZATION_FAILED",
"message": "Layer ... not supported",
"details": {}
}
}
}
```
---
### 3.3 POST `/v1/jobs/{job_id}/cancel` — 取消
**Response**
```json
{ "success": true, "data": { "job_id": "...", "status": "cancelled" } }
```
若已 completed → `400 ALREADY_COMPLETED`;若 already cancelled → `200` idempotent。
---
### 3.4 Webhook未來
Converter push 到 visionA 的 webhook URL
**Requestconverter → visionA**
```json
POST https://api.visiona.cloud/webhooks/converter
X-Converter-Signature: sha256=<hmac of body using shared secret>
Content-Type: application/json
{
"event": "job.completed" | "job.failed",
"job_id": "cvt-abc-123",
"client_job_id": "<visionA 的>",
"status": "succeeded" | "failed",
"result": { ... },
"error": { ... },
"timestamp": "..."
}
```
**visionA 驗證 signature 後**
- 更新 `converter_jobs`
- 若 succeeded下載產物存到 `storage/converter/result/`
- 建立對應的 `models` recordsource=converted, source_job_id=<client_job_id>
- 回 200
**retry 約定**
- Webhook 失敗 converter 最多重試 5 次,指數退避
- visionA 必須 idempotent 處理(用 `event + job_id` 當 key
---
## 4. 雛形階段visionA 端 stub
```go
// internal/converter/stub.go
type StubClient struct {
jobs map[string]*Job
mu sync.Mutex
}
func (s *StubClient) SubmitConvert(ctx, req) (string, error) {
s.mu.Lock()
defer s.mu.Unlock()
jobID := "stub-job-" + uuid.NewString()
s.jobs[jobID] = &Job{
ID: jobID, Status: "queued", CreatedAt: time.Now(),
TargetChip: req.TargetChip,
}
// 雛形15 秒後「完成」,指向假 result URL
time.AfterFunc(15*time.Second, func() {
s.mu.Lock()
defer s.mu.Unlock()
if j, ok := s.jobs[jobID]; ok {
j.Status = "succeeded"
j.ResultKey = "stub-result-key"
j.CompletedAt = ptrTime(time.Now())
}
})
return jobID, nil
}
```
雛形期前端可以用這個 stub 走完整 UX但不會真的產生 `.nef`
---
## 5. Error 對應表
| Converter 回傳 | visionA 前端顯示 |
|---------------|----------------|
| `UNSUPPORTED_FORMAT` | 「目前不支援此格式」|
| `INVALID_CHECKSUM` | 「檔案下載驗證失敗,請重新上傳」|
| `QUOTA_EXCEEDED` | 「本月轉檔配額已滿」|
| `QUANTIZATION_FAILED` | 「模型轉檔失敗:[detail]」|
| 其他 | 「轉檔失敗,請聯絡支援」|
---
## 6. 相容性 / 版本
- URL 含 `/v1/` 前綴,日後升級 `/v2/` 可並存
- 欄位採「新增只是 optional」原則不破壞舊版
- visionA 用 env `VISIONA_CONVERTER_API_VERSION` 切換(預設 `v1`
---
## 7. 給 Converter 團隊的確認清單
- [ ] 同意採用此 API spec
- [ ] 確認 source.type `"url"` 的 presigned URL 長度 / TTL 要求
- [ ] 確認支援的 source format 清單
- [ ] 確認 webhook push 模式的實作意願 / 時程
- [ ] 確認 rate limit / quota 政策
- [ ] 確認產物儲存位置converter 自己的 bucket還是回 visionA bucket
- [ ] 提供測試 API key
---
**雛形實作**`internal/converter/stub.go` + 前端走 stub 驗流程。
**Phase 1**`internal/converter/http.go` 實作上述 API + webhook endpoint。