283 lines
9.5 KiB
Markdown
283 lines
9.5 KiB
Markdown
# 模型参数后端化方案
|
||
|
||
## 一、当前架构(平台重构后)
|
||
|
||
平台重构已采用 **Platform Descriptor 模式**,Painting 和 Video 统一走扁平 `modelParams`:
|
||
|
||
```
|
||
src/platforms/
|
||
├── registry.js # 注册表:registerPlatform() + createPlatform()
|
||
├── painting/
|
||
│ ├── index.js # Painting descriptor(controls, state, loadConfig 等)
|
||
│ ├── modelSelector.vue
|
||
│ ├── imageUploader.vue
|
||
│ ├── models/ # 模型参数 schema(本地 JS 文件,待后端化)
|
||
│ │ ├── index.js # getModelConfig(modelName) → 查找本地 config
|
||
│ │ └── flux-2.js # 单个模型的 params 定义
|
||
│ └── controls/
|
||
│ ├── proportion.vue
|
||
│ ├── dimension.vue
|
||
│ ├── quality.vue
|
||
│ └── quantity.vue
|
||
└── video/
|
||
├── index.js # Video descriptor(仍用 fetchModelConfig 拉远程 JSON)
|
||
├── modelSelector.vue
|
||
├── imageUploader.vue
|
||
└── controls/
|
||
├── pattern.vue
|
||
├── proportion.vue
|
||
└── time.vue
|
||
```
|
||
|
||
**核心数据流(两个平台统一):**
|
||
|
||
1. 用户选择模型 → `dialogBox` 调用 `platform.loadConfig(modelName, modelType)`
|
||
2. `loadConfig` 获取参数 schema → 驱动 `controls` 数组渲染对应 UI 控件
|
||
3. 用户点击发送 → `platform.buildTaskBody({ prompt, referenceImages })` 返回扁平 `modelParams`
|
||
4. `createTask.js` 透传 `data.body`(不再做任何转换)
|
||
5. POST `/suanli/v1/tasks`,携带 `X-Session-Id` header
|
||
|
||
**当前参数配置来源(待统一):**
|
||
|
||
| 平台 | 参数配置来源 | 位置 |
|
||
|------|-------------|------|
|
||
| Painting | 本地 JS 文件(硬编码) | `src/platforms/painting/models/*.js` |
|
||
| Video | 远程静态 JSON(每日 localStorage 缓存) | `fetchModelConfig()` → `resources.xueai.art/AIGC/static/public/Platform/Video/workflows/...` |
|
||
|
||
两个平台的 `buildTaskBody()` 均已返回扁平 `modelParams`,不再构造 `{ workflowId, nodeInfoList }` 格式。
|
||
|
||
---
|
||
|
||
## 二、目标架构
|
||
|
||
将模型参数配置从**前端代码**迁移到**后端 API**,实现:
|
||
|
||
- 新增模型/修改参数无需前端发版
|
||
- Painting 和 Video 使用统一的 API 获取配置
|
||
- 前端只保留 UI 控件库,参数 schema 完全由后端驱动
|
||
|
||
```
|
||
后端 API
|
||
├── GET /api/v1/platforms/:code/models # 模型列表(已有)
|
||
│ └── 响应: [{ id, display_name, tags, config? }]
|
||
├── GET /api/v1/models/:id/config # 模型参数配置(新增)
|
||
│ └── 响应: { params: [...], inputType, maxImages, ... }
|
||
└── POST /api/v1/tasks # 创建任务(已有)
|
||
└── 请求体: 扁平 modelParams(与 RunningHub API 格式对齐)
|
||
```
|
||
|
||
**改造后的数据流:**
|
||
|
||
```
|
||
用户选择模型
|
||
→ platform.loadConfig(modelName)
|
||
→ GET /api/v1/models/:id/config
|
||
→ 返回 { params: [{ name, ui, default, options, ... }] }
|
||
→ 前端根据 ui 字段渲染对应控件
|
||
→ 用户填写参数
|
||
→ buildTaskBody() 返回扁平 { aspectRatio, resolution, ... }
|
||
→ POST /api/v1/tasks
|
||
```
|
||
|
||
---
|
||
|
||
## 三、模型配置 API 格式
|
||
|
||
### 3.1 请求
|
||
|
||
```
|
||
GET /api/v1/models/:modelId/config
|
||
```
|
||
|
||
`modelId` 来自模型列表接口返回的 `id` 字段(UUID)。
|
||
|
||
### 3.2 响应格式
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"inputType": "text",
|
||
"maxImages": 4,
|
||
"promptPlaceholder": "描述你想生成的画面和动作。",
|
||
"params": [
|
||
{
|
||
"name": "prompt",
|
||
"ui": "textarea",
|
||
"label": "提示词",
|
||
"required": true,
|
||
"default": ""
|
||
},
|
||
{
|
||
"name": "aspectRatio",
|
||
"ui": "proportion",
|
||
"label": "比例",
|
||
"default": "1:1",
|
||
"options": ["1:1", "3:4", "4:3", "9:16", "16:9", "custom"]
|
||
},
|
||
{
|
||
"name": "resolution",
|
||
"ui": "resolution",
|
||
"label": "分辨率",
|
||
"default": "2k",
|
||
"options": ["1k", "2k", "4k"]
|
||
},
|
||
{
|
||
"name": "size",
|
||
"ui": "dimension",
|
||
"label": "尺寸",
|
||
"default": "1024*1024",
|
||
"dimension": {
|
||
"separator": "*",
|
||
"width": { "min": 256, "max": 6197 },
|
||
"height": { "min": 256, "max": 4096 }
|
||
}
|
||
},
|
||
{
|
||
"name": "quality",
|
||
"ui": "select",
|
||
"label": "画质",
|
||
"default": "medium",
|
||
"options": ["low", "medium", "high"]
|
||
},
|
||
{
|
||
"name": "quantity",
|
||
"ui": "quantity",
|
||
"label": "生成数量",
|
||
"default": 1,
|
||
"options": [1, 2, 3, 4]
|
||
},
|
||
{
|
||
"name": "imageUrl",
|
||
"ui": "imageUpload",
|
||
"label": "参考图",
|
||
"maxCount": 4
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.3 `ui` 字段与前端控件映射
|
||
|
||
| `ui` 值 | 前端控件 | 说明 |
|
||
|---------|---------|------|
|
||
| `textarea` | Sender 内置 textarea | 提示词输入框,不需要额外渲染控件 |
|
||
| `proportion` | `PaintingProportion` / `VideoProportion` | 比例选择 Popover,`options` 含 `custom` 时允许自定义宽高 |
|
||
| `resolution` | `proportion` 控件内部 | 分辨率子选项,与 proportion 共用 Popover |
|
||
| `dimension` | `DimensionInput` | **组合模式**:单字段 `"W*H"` 格式,通过 `dimension.parse/format` 序列化 |
|
||
| `dimensionWidth` + `dimensionHeight` | `DimensionInput` | **拆分模式**:两个独立字段,共享同一个 Popover 和比例锁 |
|
||
| `select` | `Select` | 通用下拉选择(如 quality) |
|
||
| `quantity` | `Quantity` | 生成数量,上限由 `options` 最大值派生 |
|
||
| `imageUpload` | `ImageUploader` | 参考图上传,`maxCount` 控制数量上限 |
|
||
| `hidden` | 无 | 静默写入默认值,不渲染控件 |
|
||
|
||
> **关于 `dimension.separator`:** 当前前端 Painting 配置中 `parse/format` 为 JS 函数(`s.split('*')` / `` `${w}*${h}` ``),后端返回 JSON 无法携带函数。因此 `dimension` 类型的配置需包含 `separator` 字段,前端据此在运行时生成等价的 parse/format 逻辑,无需硬编码分隔符。拆分模式(`dimensionWidth` + `dimensionHeight`)无此字段。
|
||
|
||
### 3.4 条件显示
|
||
|
||
```json
|
||
{
|
||
"name": "customWidth",
|
||
"ui": "dimensionWidth",
|
||
"showWhen": { "aspectRatio": "custom" }
|
||
}
|
||
```
|
||
|
||
`showWhen` 字段使参数仅在指定条件满足时显示。当前支持的条件:`aspectRatio` 值为 `custom`。
|
||
|
||
---
|
||
|
||
## 四、迁移步骤
|
||
|
||
### 阶段一:后端实现模型配置 API
|
||
|
||
- [ ] 设计并实现 `GET /api/v1/models/:id/config` 接口
|
||
- [ ] 将 Painting 的 9 个模型配置(`src/platforms/painting/models/*.js`)迁移到数据库/配置文件
|
||
- [ ] 将 Video 的 workflow 配置(`resources.xueai.art/AIGC/static/public/Platform/Video/workflows/...`)迁移到同一接口
|
||
- [ ] 响应格式对齐 3.2 节的 schema
|
||
|
||
### 阶段二:前端适配
|
||
|
||
- [ ] `src/platforms/painting/models/` 目录删除,`getModelConfig()` 改为调用 API
|
||
- [ ] `src/utils/modelConfig.js`(Video 旧架构遗留)删除,Video descriptor 改为调用同一 API
|
||
- [ ] `src/utils/createTask.js`(当前仅透传)删除,`dialogBox` 直接使用 `buildTaskBody()` 返回值
|
||
- [ ] Painting descriptor 的 `loadConfig()` 改为 `fetch` API + 本地缓存(30s TTL,与模型列表一致)
|
||
|
||
### 阶段三:清理
|
||
|
||
- [ ] 删除 `src/utils/modelConfig.js`
|
||
- [ ] 删除 `src/utils/createTask.js`
|
||
- [ ] 确认前端不再包含任何硬编码的模型参数
|
||
|
||
---
|
||
|
||
## 五、与 RunningHub API 的关系
|
||
|
||
后端创建任务的请求体格式应**直接对齐 RunningHub 标准模型 API**,前端 `buildTaskBody()` 返回的扁平 `modelParams` 即是 API body:
|
||
|
||
```json
|
||
{
|
||
"prompt": "...",
|
||
"resolution": "720p",
|
||
"aspectRatio": "16:9",
|
||
"duration": 5
|
||
}
|
||
```
|
||
|
||
不再需要中间层转换(旧架构的 `src/config/runninghub/` 已删除)。
|
||
|
||
---
|
||
|
||
## 附录:RunningHub API 参考
|
||
|
||
以下为 RunningHub 标准模型 API 的原始文档,作为后端任务创建接口的设计参考。
|
||
|
||
### A.1 提交任务
|
||
|
||
```curl
|
||
curl --location --request POST 'https://www.runninghub.cn/openapi/v2/rhart-video/ltx-2.3/text-to-video' \
|
||
--header "Content-Type: application/json" \
|
||
--header "Authorization: Bearer ${RUNNINGHUB_API_KEY}" \
|
||
--data-raw '{
|
||
"prompt": "...",
|
||
"resolution": "720p",
|
||
"aspectRatio": "16:9",
|
||
"duration": 5
|
||
}'
|
||
```
|
||
|
||
| 参数 | 类型 | 必填/可选 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `prompt` | String | 必填 | 提示词 |
|
||
| `resolution` | String | 必填 | 枚举值: [1080p, 720p, 480p] |
|
||
| `aspectRatio` | String | 必填 | 枚举值: [16:9, 9:16] |
|
||
| `duration` | Int | 必填 | 输入范围值: 5 - 15 |
|
||
|
||
### A.2 查询结果
|
||
|
||
```curl
|
||
curl --location --request POST 'https://www.runninghub.cn/openapi/v2/query' \
|
||
--header "Content-Type: application/json" \
|
||
--header "Authorization: Bearer ${RUNNINGHUB_API_KEY}" \
|
||
--data-raw '{"taskId": "${RUNNINGHUB_TASKID}"}'
|
||
```
|
||
|
||
响应字段:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `taskId` | String | 任务 ID |
|
||
| `status` | String | QUEUED / RUNNING / SUCCESS / FAILED |
|
||
| `results` | List | 生成结果列表 |
|
||
| `results[].url` | String | 结果 URL(24 小时有效) |
|
||
| `results[].nodeId` | String | 工作流节点 ID |
|
||
| `results[].outputType` | String | 文件扩展名 (png, mp4, txt) |
|
||
| `results[].text` | String | 纯文本输出内容 |
|
||
|
||
### A.3 文件上传
|
||
|
||
**上传接口:** `POST https://www.runninghub.cn/openapi/v2/media/upload/binary`
|
||
|
||
支持 `imageUrls`(公共 URL)、Base64 Data URI、RH 上传接口三种方式传入图片。
|