依 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)。
7.4 KiB
7.4 KiB
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 URL,converter 完成後回呼
雛形先用 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:
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 URL,converter 自己下載 |
source.checksum_sha256 |
✓ | visionA 計算好的 sha256,converter 下載後驗證 |
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 id,converter 回傳時要帶上 |
Response 202:
{
"success": true,
"data": {
"job_id": "cvt-abc-123", // converter 側的 id
"status": "queued",
"accepted_at": "2026-04-21T12:00:00Z",
"estimated_duration_seconds": 120
}
}
Response 錯誤:
{
"success": false,
"error": {
"code": "UNSUPPORTED_FORMAT" | "INVALID_CHECKSUM" | "SOURCE_UNREACHABLE" | "QUOTA_EXCEEDED",
"message": "..."
}
}
3.2 GET /v1/jobs/{job_id} — 查詢狀態
Response 200:
{
"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:
{ "success": true, "data": { "job_id": "...", "status": "cancelled" } }
若已 completed → 400 ALREADY_COMPLETED;若 already cancelled → 200 idempotent。
3.4 Webhook(未來)
Converter push 到 visionA 的 webhook URL:
Request(converter → visionA):
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/ - 建立對應的
modelsrecord(source=converted, source_job_id=<client_job_id>) - 回 200
retry 約定:
- Webhook 失敗 converter 最多重試 5 次,指數退避
- visionA 必須 idempotent 處理(用
event + job_id當 key)
4. 雛形階段(visionA 端 stub)
// 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。