Files
observer/server/biz/model/dto/training.go
T
2026-08-28 14:31:13 +08:00

409 lines
19 KiB
Go
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.
package dto
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
)
// 模型训练体系接口(数据集 → 标注 → 训练 → 模型版本),组前缀 /api/v1/admin。
// 图片/标注/模型文件均在服务器磁盘(app.datasetDir),DB 只存元数据。
// 图片访问为直接写响应体场景,由 controller 以 *ghttp.Request 方法实现
// (见 controller/admin.go 的 Image 方法)。
// ---------- 数据集管理 ----------
// AdminDatasetListReq 数据集列表(创建时间倒序)
type AdminDatasetListReq struct {
g.Meta `path:"/datasets" method:"get" summary:"数据集列表" tags:"管理端"`
Keyword string `json:"keyword" v:"length:0,50" dc:"名称模糊匹配"`
Page int `json:"page" v:"integer|min:1" dc:"页码,默认 1"`
Size int `json:"size" v:"integer|min:1|max:100" dc:"每页条数,默认 20"`
}
// AdminDatasetItem 数据集条目(卡片展示用;AI 端点/训练机 SSH 走 config.yml 全局配置(localAi / training.ssh))
type AdminDatasetItem struct {
Id int64 `json:"id"`
Name string `json:"name"`
Source string `json:"source"` // manual | ai
ImageCount int64 `json:"imageCount"`
LabeledCount int64 `json:"labeledCount"`
Status string `json:"status"` // building | labeled | synced
Cover string `json:"cover"` // 封面文件名
Description string `json:"description"` // 描述
NamePrefix string `json:"namePrefix"` // AI 生成图文件名前缀
TrainingId int64 `json:"trainingId"` // 最新训练记录 id(发布/详情用)
TrainingStatus string `json:"trainingStatus"` // 最新训练记录状态 running|success|failed|空
TrainingError string `json:"trainingError"` // 最新训练记录失败原因(failed 时展示用)
TrainingPublished bool `json:"trainingPublished"` // 该训练是否已发布过版本(已发布不再显示发布按钮)
TrainingCurrentEpoch int `json:"trainingCurrentEpoch"`
TrainingTotalEpochs int `json:"trainingTotalEpochs"`
CreatedAt *gtime.Time `json:"createdAt"`
UpdatedAt *gtime.Time `json:"updatedAt"`
}
type AdminDatasetListRes struct {
Total int64 `json:"total"`
List []*AdminDatasetItem `json:"list"`
}
// AdminDatasetUpdateReq 更新数据集配置(空值字段不覆盖原值,保留现有配置;改名时同步迁移
// 图片目录与模型文件,名称唯一且同是磁盘目录名;
// AI 端点/训练机 SSH 走 config.yml 全局配置(localAi / training.ssh))
type AdminDatasetUpdateReq struct {
g.Meta `path:"/datasets/update" method:"post" summary:"更新数据集配置" tags:"管理端"`
Id int64 `json:"id" v:"required|min:1" dc:"数据集 id"`
Name string `json:"name" v:"length:0,50" dc:"数据集名称(非空且变化才更新;同是目录名)"`
NamePrefix string `json:"namePrefix" v:"length:0,50" dc:"AI 生成图文件名前缀(非空才更新)"`
Description string `json:"description" v:"length:0,500" dc:"描述(非空才更新)"`
Cover string `json:"cover" v:"length:0,200" dc:"封面文件名(非空才更新)"`
}
type AdminDatasetUpdateRes struct {
Id int64 `json:"id"`
}
// AdminDatasetCreateReq 新建数据集(名称同时是磁盘目录名:中文/字母/数字/下划线/短横线,唯一)
type AdminDatasetCreateReq struct {
g.Meta `path:"/datasets" method:"post" summary:"新建数据集" tags:"管理端"`
Name string `json:"name" v:"required|regex:^[a-zA-Z0-9_一-龥-]+$|length:1,50" dc:"数据集名称(同是目录名,唯一)"`
Source string `json:"source" v:"required|in:manual,ai" dc:"图片来源 manual|ai"`
NamePrefix string `json:"namePrefix" v:"length:0,50" dc:"AI 生成图文件名前缀(可选,字母/数字/下划线/短横线)"`
}
type AdminDatasetCreateRes struct {
Id int64 `json:"id"`
}
// AdminDatasetDeleteReq 删除数据集(删除图片文件+标注目录+记录;AI 生成图为付费资产,前端须带确认文案)
type AdminDatasetDeleteReq struct {
g.Meta `path:"/datasets/delete" method:"post" summary:"删除数据集" tags:"管理端"`
Id int64 `json:"id" v:"required|min:1" dc:"数据集 id"`
}
type AdminDatasetDeleteRes struct{}
// AdminDatasetUploadReq 上传图片(multipart 多文件;重名跳过并计数)
type AdminDatasetUploadReq struct {
g.Meta `path:"/datasets/upload" method:"post" summary:"上传图片" tags:"管理端" mime:"multipart/form-data"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
Files []*ghttp.UploadFile `json:"files" dc:"图片文件(jpg/jpeg/png,多选)"`
}
type AdminDatasetUploadRes struct {
Added int `json:"added"`
Skipped int `json:"skipped"` // 重名跳过数
}
// AdminDatasetGenerateReq AI 生成图片(同步执行;prompt 可手填覆盖模板,禁止含目标位置描述;
// 未手填走 imageGen.promptTemplate 组装:species/scene 用表单选择,数量词=animalCount。
// distanceMin/distanceMax(米)与 animalCount 填写后逐张校验:RF-DETR 检测目标框(去重口径),
// 数量精确匹配、估算距离(物理公式)越界自动重新生成(上限 consts.GenValidateMaxAttempts 次)。
// 文件名前缀为数据集属性(datasets 表 name_prefix),不再随生成请求传入
type AdminDatasetGenerateReq struct {
g.Meta `path:"/datasets/generate" method:"post" summary:"AI 生成图片" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
Species string `json:"species" v:"length:0,50" dc:"动物名称(如家鸽/山斑鸠;留空按数据集名取物种池随机)"`
Prompt string `json:"prompt" v:"length:0,500" dc:"完整提示词(可选,填写则覆盖模板;禁止含位置描述)"`
Count int `json:"count" v:"required|integer|min:1|max:8" dc:"生成张数 1-8"`
Size string `json:"size" v:"required|in:704x1248" dc:"图片尺寸"`
DistanceMin int `json:"distanceMin" v:"integer|min:0" dc:"距离范围下限(米,0=不校验距离)"`
DistanceMax int `json:"distanceMax" v:"integer|min:0" dc:"距离范围上限(米,0=不校验距离)"`
AnimalCount int `json:"animalCount" v:"integer|min:0|max:20" dc:"画面目标数量(精确匹配,0=不校验;标注框数不超过该值)"`
}
// AdminDatasetGenOptionsReq 生成表单选项(物种池/场景池,来自 config imageGen 配置)
type AdminDatasetGenOptionsReq struct {
g.Meta `path:"/datasets/gen-options" method:"get" summary:"生成表单选项" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
}
type AdminDatasetGenOptionsRes struct {
Species []string `json:"species"` // 该数据集的物种池(未配置则为空,表单可自由输入)
Scenes []string `json:"scenes"` // 环境场景池
}
type AdminDatasetGenerateRes struct {
Generated int `json:"generated"` // 成功张数(失败时可能 < count,已成功的图保留)
}
// AdminDatasetImagesReq 数据集图片列表(标注工作台/网格预览用)
type AdminDatasetImagesReq struct {
g.Meta `path:"/datasets/images" method:"get" summary:"数据集图片列表" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
}
// AdminImageItem 图片条目(url 为管理端图片访问地址,前端拼 apiBaseUrl 使用)
type AdminImageItem struct {
Id int64 `json:"id"`
Filename string `json:"filename"`
Source string `json:"source"` // manual | ai
Prompt string `json:"prompt"`
Url string `json:"url"`
CreatedAt *gtime.Time `json:"createdAt"`
}
type AdminDatasetImagesRes struct {
List []*AdminImageItem `json:"list"`
}
// AdminDatasetImagesDeleteReq 删除图片(删文件+删记录;付费资产,前端须带确认文案)
type AdminDatasetImagesDeleteReq struct {
g.Meta `path:"/datasets/images/delete" method:"post" summary:"删除图片" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
Ids []int64 `json:"ids" v:"required|min-length:1" dc:"图片记录 id 列表"`
}
type AdminDatasetImagesDeleteRes struct{}
// AdminDatasetImageReq 图片访问(直写响应体,例外场景:controller 经 service 校验归属后输出二进制)
type AdminDatasetImageReq struct {
g.Meta `path:"/datasets/image" method:"get" summary:"图片访问" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
Filename string `json:"filename" v:"required" dc:"图片文件名"`
}
type AdminDatasetImageRes struct{}
// AdminDatasetCoverUploadReq 上传数据集封面(multipart 单文件;覆盖旧封面,UUID 命名 jpg)
type AdminDatasetCoverUploadReq struct {
g.Meta `path:"/datasets/cover" method:"post" summary:"上传数据集封面" tags:"管理端" mime:"multipart/form-data"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
File *ghttp.UploadFile `json:"file" dc:"封面图片(jpg/jpeg/png,≤2MB)"`
}
type AdminDatasetCoverUploadRes struct{}
// AdminDatasetCoverReq 封面访问(直写响应体,与 Image 同模式)
type AdminDatasetCoverReq struct {
g.Meta `path:"/datasets/cover" method:"get" summary:"数据集封面访问" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
}
type AdminDatasetCoverRes struct{}
// AdminDatasetCoverDeleteReq 删除数据集封面(删文件+清字段;列表卡片恢复占位图)
type AdminDatasetCoverDeleteReq struct {
g.Meta `path:"/datasets/cover/delete" method:"post" summary:"删除数据集封面" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
}
type AdminDatasetCoverDeleteRes struct{}
// ---------- 训练编排 ----------
// AdminTrainingListReq 训练任务列表(创建时间倒序)
type AdminTrainingListReq struct {
g.Meta `path:"/trainings" method:"get" summary:"训练任务列表" tags:"管理端"`
Status string `json:"status" v:"in:running,success,failed" dc:"状态筛选"`
Page int `json:"page" v:"integer|min:1" dc:"页码,默认 1"`
Size int `json:"size" v:"integer|min:1|max:100" dc:"每页条数,默认 20"`
}
// AdminTrainingItem 训练任务条目(列表不含日志尾部,详情接口返回)
type AdminTrainingItem struct {
Id int64 `json:"id"`
Name string `json:"name"`
DatasetId int64 `json:"datasetId"`
DatasetName string `json:"datasetName"`
Status string `json:"status"` // running | success | failed
Imgsz int `json:"imgsz"`
Epochs int `json:"epochs"`
Batch int `json:"batch"`
Device string `json:"device"`
CurrentEpoch int `json:"currentEpoch"`
TotalEpochs int `json:"totalEpochs"`
Metrics string `json:"metrics"`
Error string `json:"error"`
StartedAt *gtime.Time `json:"startedAt"`
FinishedAt *gtime.Time `json:"finishedAt"`
CreatedAt *gtime.Time `json:"createdAt"`
}
type AdminTrainingListRes struct {
Total int64 `json:"total"`
List []*AdminTrainingItem `json:"list"`
}
// AdminTrainingStartReq 发起训练(并发度 1:已有 running 任务时拒绝;训练参数走 config.yml training 节点,name 空则自动生成)
type AdminTrainingStartReq struct {
g.Meta `path:"/trainings" method:"post" summary:"发起训练" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
Name string `json:"name" v:"length:0,50" dc:"任务名称,空自动生成"`
}
type AdminTrainingStartRes struct {
Id int64 `json:"id"`
}
// AdminTrainingDetailReq 训练任务详情(含日志尾部)
type AdminTrainingDetailReq struct {
g.Meta `path:"/trainings/detail" method:"get" summary:"训练任务详情" tags:"管理端"`
Id int64 `json:"id" v:"required|min:1" dc:"训练任务 id"`
}
// AdminTrainingDetailRes 详情 = 条目 + 日志尾部
type AdminTrainingDetailRes struct {
AdminTrainingItem
LogTail string `json:"logTail"`
}
// AdminTrainingCancelReq 取消训练(杀进程,任务置 failed)
type AdminTrainingCancelReq struct {
g.Meta `path:"/trainings/cancel" method:"post" summary:"取消训练" tags:"管理端"`
Id int64 `json:"id" v:"required|min:1" dc:"训练任务 id"`
}
type AdminTrainingCancelRes struct{}
// AdminTrainingPublishReq 发布模型版本(仅 success 任务;按数据集版本号 m<major>.<minor>.<patch> 自增)
type AdminTrainingPublishReq struct {
g.Meta `path:"/trainings/publish" method:"post" summary:"发布模型版本" tags:"管理端"`
Id int64 `json:"id" v:"required|min:1" dc:"训练任务 id"`
}
type AdminTrainingPublishRes struct {
Version string `json:"version"`
}
// ---------- 模型版本 ----------
// ---------- 预标注 ----------
// AdminLabelTaskListReq 预标注任务列表(创建时间倒序)
type AdminLabelTaskListReq struct {
g.Meta `path:"/label-tasks" method:"get" summary:"预标注任务列表" tags:"管理端"`
Page int `json:"page" v:"integer|min:1" dc:"页码,默认 1"`
Size int `json:"size" v:"integer|min:1|max:100" dc:"每页条数,默认 20"`
}
type AdminLabelTaskItem struct {
Id int64 `json:"id"`
DatasetId int64 `json:"datasetId"`
DatasetName string `json:"datasetName"`
Status string `json:"status"` // running | done
Total int `json:"total"`
Done int `json:"done"`
Error string `json:"error"`
CreatedAt *gtime.Time `json:"createdAt"`
FinishedAt *gtime.Time `json:"finishedAt"`
}
type AdminLabelTaskListRes struct {
Total int64 `json:"total"`
List []*AdminLabelTaskItem `json:"list"`
}
// AdminLabelTaskStartReq 发起预标注(RF-DETR 扫描,候选框合并入 datasets/<name>/boxes.json
type AdminLabelTaskStartReq struct {
g.Meta `path:"/label-tasks" method:"post" summary:"发起预标注" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
Filenames []string `json:"filenames" v:"length:0,500" dc:"选中图片列表(多选批量);缺省=全量扫描"`
}
type AdminLabelTaskStartRes struct {
Id int64 `json:"id"`
}
// AdminImageVlmReviewReq 单图 VLM 补检:qwen3.8-9b 按「环境/光线/习性」推理 RF-DETR 之外
// 可能藏匿目标的位置(排除已确认坐标),最多补 3 个疑似框(class 1)追加到 labels_json。
// 依赖 localAi.vlmModel(默认 qwen3.8-9b);VL 与 z-image 生成不能同时驻留显存,批量补检
// 应在生成队列空闲时执行
type AdminImageVlmReviewReq struct {
g.Meta `path:"/datasets/images/vlm-review" method:"post" summary:"VLM 藏匿位补检" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
ImageId int64 `json:"imageId" v:"required|min:1" dc:"图片 id"`
}
type AdminImageVlmReviewRes struct {
Added int `json:"added"` // 新增疑似框数(≤3)
Boxes []*AdminLabelBox `json:"boxes"` // VLM 新增框(归一化坐标,class=1)
Note string `json:"note"` // 未补检原因(如已确认框为空/VL 无输出)
}
// AdminLabelTaskDetailReq 预标注任务详情(每张图候选框 + 已确认标注,供工作台 canvas 叠框)
type AdminLabelTaskDetailReq struct {
g.Meta `path:"/label-tasks/detail" method:"get" summary:"预标注任务详情" tags:"管理端"`
Id int64 `json:"id" v:"required|min:1" dc:"标注任务 id"`
}
// AdminLabelBox 单框(归一化 xywh,坐标 0~1)
type AdminLabelBox struct {
Cx float64 `json:"cx"`
Cy float64 `json:"cy"`
W float64 `json:"w"`
H float64 `json:"h"`
Confidence float64 `json:"confidence"` // 人工框为 1
Class int `json:"class"` // 0 确认 | 1 疑似
}
// AdminLabelImageItem 工作台单张图:全部标注(labels_json,AI 自动标注与人工框同层,人工可修改/清理)
type AdminLabelImageItem struct {
Filename string `json:"filename"`
Url string `json:"url"`
Width int `json:"width"`
Height int `json:"height"`
Boxes []*AdminLabelBox `json:"boxes"`
Labeled bool `json:"labeled"` // 有标注(AI 自动标注或人工保存)
}
type AdminLabelTaskDetailRes struct {
TaskId int64 `json:"taskId"`
DatasetId int64 `json:"datasetId"`
DatasetName string `json:"datasetName"`
Status string `json:"status"`
Total int `json:"total"`
Done int `json:"done"`
Error string `json:"error"`
Images []*AdminLabelImageItem `json:"images"`
}
// AdminLabelWorkbenchReq 标注工作台数据(无历史任务时直接用数据集图片 + 已确认标注,
// 候选框读数据集目录 boxes.json)
type AdminLabelWorkbenchReq struct {
g.Meta `path:"/label-workbench" method:"get" summary:"标注工作台数据" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
}
type AdminLabelWorkbenchRes struct {
DatasetId int64 `json:"datasetId"`
DatasetName string `json:"datasetName"`
Images []*AdminLabelImageItem `json:"images"`
}
// AdminLabelSaveReq 保存单张图标注(工作台「保存本张」;覆写 dataset_image.labels_json
type AdminLabelSaveReq struct {
g.Meta `path:"/label-tasks/save" method:"post" summary:"保存图片标注" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
Filename string `json:"filename" v:"required|length:1,200" dc:"图片文件名"`
Boxes []*AdminLabelBox `json:"boxes" dc:"确认后的框列表(空=清空标注)"`
}
type AdminLabelSaveRes struct {
LabeledCount int64 `json:"labeledCount"` // 该数据集当前已标注图数
}
// ---------- 客户端模型目录 ----------
// ModelCatalogReq 模型目录(客户端登录态):全部数据集的当前生效模型
type ModelCatalogReq struct {
g.Meta `path:"/models" method:"get" summary:"模型目录" tags:"客户端"`
}
// ModelCatalogItem 客户端模型条目(App 按需下载,多模型并行推理合并)
type ModelCatalogItem struct {
DatasetId int64 `json:"datasetId"`
DatasetName string `json:"datasetName"`
Version string `json:"version"`
Labels []string `json:"labels"` // 类别名,App 推理结果展示用
SizeBytes int64 `json:"sizeBytes"`
Sha256 string `json:"sha256"`
Notes string `json:"notes"`
PublishedAt *gtime.Time `json:"publishedAt"`
DownloadUrl string `json:"downloadUrl"` // /download/trainings/<name>.tflite
}
type ModelCatalogRes struct {
List []*ModelCatalogItem `json:"list"`
}