add stable media support and sync skillhub UI
This commit is contained in:
+24
-9
@@ -14,7 +14,6 @@
|
||||
|
||||
所以 `popiartServer` 不再重复持有:
|
||||
|
||||
- 本地 artifact blob store
|
||||
- 本地 artifact metadata table
|
||||
- 本地 job_logs table
|
||||
|
||||
@@ -23,6 +22,7 @@
|
||||
- `sessions`
|
||||
- `jobs`(更准确说是 job refs)
|
||||
- `skill_routes`
|
||||
- `media blobs + metadata`
|
||||
|
||||
## 当前关系
|
||||
|
||||
@@ -30,14 +30,16 @@
|
||||
session -> jobs
|
||||
jobs -> result_refs_json
|
||||
project -> skill_routes
|
||||
media -> local files + metadata json
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
- `session` 保存 PopiArt 登录态和对应的 `PopiNewAPI token`
|
||||
- `jobs` 保存 skill 语义、用户归属、上游引用和同步结果引用
|
||||
- `result_refs_json` 保存同步能力的返回引用,例如 `data_url` 或远端 `url`
|
||||
- `result_refs_json` 保存同步能力的结果引用;新结果会优先 re-host 到本地 media store
|
||||
- `skill_routes` 保存项目级路由覆盖
|
||||
- `media` 保存稳定 URL 所需的本地文件与元数据
|
||||
|
||||
## 存储位置
|
||||
|
||||
@@ -66,7 +68,10 @@ POPIART_SQLITE_PATH=./data/popiart.db
|
||||
2. `JobRepository`
|
||||
3. `RouteRepository`
|
||||
|
||||
没有单独的 blob storage 抽象。
|
||||
本地开发版没有对象存储依赖,但现在有一个轻量 media 存储层:
|
||||
|
||||
- blob 文件保存在 `POPIART_DATA_DIR/media/blobs/`
|
||||
- metadata JSON 保存在 `POPIART_DATA_DIR/media/meta/`
|
||||
|
||||
## SQLite Schema
|
||||
|
||||
@@ -141,15 +146,24 @@ CREATE TABLE skill_routes (
|
||||
### 完成同步结果 job
|
||||
|
||||
1. 如果 `PopiNewAPI` 直接返回同步结果,例如 `b64_json` 或 `url`
|
||||
2. `popiartServer` 只把它转换成 `result_refs_json`
|
||||
3. `GET /jobs/:id/artifacts` 时再从 `result_refs_json` 派生 artifact 列表
|
||||
2. `popiartServer` 会先把结果 re-host 到本地 media store
|
||||
3. 再把本地 `local_path + media_id + stable url` 写进 `result_refs_json`
|
||||
4. `GET /jobs/:id/artifacts` 时再从 `result_refs_json` 派生 artifact 列表
|
||||
|
||||
### 拉取 artifact
|
||||
|
||||
1. server 从 `artifact_id` 反推出 `job_id + result index`
|
||||
2. 读取 `jobs.result_refs_json`
|
||||
3. 如果是 `data_url`,直接解码并流式返回
|
||||
4. 如果是远端 `url`,server 代理下载并返回
|
||||
3. 如果是本地 `local_path`,直接读取本地文件并流式返回
|
||||
4. 如果是旧的 `data_url`,直接解码并流式返回
|
||||
5. 如果是旧的远端 `url`,server 代理下载并返回
|
||||
|
||||
### 读取 media
|
||||
|
||||
1. `POST /v1/media/upload` 会把本地文件写入 `media/blobs/`
|
||||
2. server 同时写一份 metadata JSON 到 `media/meta/`
|
||||
3. `GET /v1/media/:id` 返回 media 元数据
|
||||
4. `GET /v1/media/:id/content` 返回可供模型或客户端直接 fetch 的稳定内容路径
|
||||
|
||||
## 当前边界
|
||||
|
||||
@@ -157,7 +171,8 @@ CREATE TABLE skill_routes (
|
||||
- `session` 会保留
|
||||
- `job ref` 会保留
|
||||
- `artifact` 通过 `result_refs_json` 继续可读
|
||||
- 新 `media` 文件和 metadata 会继续可读
|
||||
- 已存在的 `pending/running` job 仍然不会自动恢复执行
|
||||
- 视频类 `upstream_task` 路径只预留了字段,后续再接 `PopiNewAPI` task 查询
|
||||
- 对同步图像结果来说,`data_url` 仍然会落在 SQLite 的 `result_refs_json`
|
||||
这是当前 `PopiNewAPI` 没有统一 file id 的现实折中,但已经不再有本地 artifact 表和 blob store
|
||||
- 旧数据里仍可能存在 `data_url` 或上游 `url`
|
||||
- 新写入路径优先落本地 media store,从而给 artifact 补出稳定 `url`
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
| 项目 | 角色 | 应该负责 | 不应该负责 |
|
||||
|---|---|---|---|
|
||||
| `popiartcli` | 面向 coding agent 和创作者的统一 CLI 入口 | 登录、发现 skill、调用 skill、查看 jobs、拉取 artifacts、本地配置 | 不直接持有上游 provider key,不直接做模型路由,不直接做供应商计费 |
|
||||
| `popiartServer` | PopiArt 产品后端 | 用户鉴权、项目权限、skill 注册表聚合、skill 执行、job 引用管理、artifact read-through、路由决策、计费归因 | 不把供应商细节暴露给 CLI,不把 skillhub 直接耦合到 CLI,不重复实现 `PopiNewAPI` 已有的模型网关能力 |
|
||||
| `popiartServer` | PopiArt 产品后端 | 用户鉴权、项目权限、skill 注册表聚合、skill 执行、job 引用管理、artifact 与 media 管理、稳定 URL 生成、路由决策、计费归因 | 不把供应商细节暴露给 CLI,不把 skillhub 直接耦合到 CLI,不重复实现 `PopiNewAPI` 已有的模型网关能力 |
|
||||
| `PopiNewAPI` | 模型网关和通道管理层 | 管理上游渠道和 key、代理模型请求、记录原始用量、提供模型层能力 | 不承载 PopiArt 的 skill 业务语义,不负责 CLI 交互,不负责产品级项目上下文 |
|
||||
|
||||
## 标准调用链路
|
||||
@@ -58,7 +58,8 @@ GitHub skillhub / skillhub.popi.art
|
||||
- 上游 provider key 管理:`PopiNewAPI`
|
||||
- 原始模型调用计量:`PopiNewAPI`
|
||||
- 面向 skill / project / user 的计费归因:`popiartServer`
|
||||
- artifact 文件存储与 task 内容代理:优先复用 `PopiNewAPI`,`popiartServer` 只做 read-through
|
||||
- artifact 与 media 的产品级持久化、稳定 URL 和生命周期:`popiartServer`
|
||||
- provider 专属任务代理、task 内容代理与供应商差异适配:`PopiNewAPI`
|
||||
|
||||
一个重要原则是:
|
||||
|
||||
@@ -96,6 +97,7 @@ CLI 只拿产品层 key;后端再用自己的方式调用 `PopiNewAPI`。
|
||||
|
||||
- 图生图
|
||||
- 图生视频
|
||||
- 稳定媒体 URL 与 artifact/media 复用
|
||||
- 更多供应商和项目级路由覆盖
|
||||
|
||||
## 什么时候改哪个仓库
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# popiartServer Stable Media URL V1
|
||||
|
||||
这份文档只描述 `popiartServer` 这一层为了支持稳定媒体 URL 所做的职责扩展,不覆盖 `popiartcli` 的命令面,也不要求修改 `PopiNewAPI` 的现有通道实现。
|
||||
|
||||
## 背景
|
||||
|
||||
原有本地开发版 `popiartServer` 更偏向:
|
||||
|
||||
- `artifact` read-through
|
||||
- `result_refs_json` 存 `data_url` 或上游临时 `url`
|
||||
- `GET /artifacts/:id/content` 由 server 当场去解码或代理下载
|
||||
|
||||
这种实现能跑通基本流程,但不适合多模态 skill 的复用:
|
||||
|
||||
- `img2img` 经常还要重新拉流或转 base64
|
||||
- 上游签名 URL 可能过期
|
||||
- 前一个 job 的输出不能稳定地作为下一个 job 的 URL 输入
|
||||
|
||||
## V1 目标
|
||||
|
||||
`popiartServer` 在不依赖外部对象存储的前提下,先提供一套本地可运行的稳定媒体 URL 能力:
|
||||
|
||||
- 本地上传文件可立即获得稳定 URL
|
||||
- 新生成的 job 结果会被 re-host 到本地 media store
|
||||
- 新 artifact 会携带 `media_id` 和 `url`
|
||||
- `image2video` 的 `vidu*` 路由可以直接复用 artifact URL
|
||||
|
||||
## 新接口
|
||||
|
||||
### `POST /v1/media/upload`
|
||||
|
||||
上传一个本地文件,直接创建 media 记录并返回:
|
||||
|
||||
- `id`
|
||||
- `project_id`
|
||||
- `filename`
|
||||
- `content_type`
|
||||
- `size_bytes`
|
||||
- `created_at`
|
||||
- `url`
|
||||
- `visibility`
|
||||
- `sha256`
|
||||
|
||||
### `GET /v1/media/:id`
|
||||
|
||||
读取 media 元数据。这个接口需要登录态,并要求 media 属于当前用户。
|
||||
|
||||
### `GET /v1/media/:id/content`
|
||||
|
||||
读取稳定内容 URL。这个接口默认允许匿名 GET,以便模型提供商可以直接 fetch。
|
||||
|
||||
## Artifact 行为变化
|
||||
|
||||
`POST /v1/artifacts/upload` 现在不再只把内容塞成 `data_url`:
|
||||
|
||||
1. server 先把上传文件写入本地 media store
|
||||
2. 再创建本地 upload job
|
||||
3. 最终 artifact 返回:
|
||||
- `id`
|
||||
- `media_id`
|
||||
- `url`
|
||||
- `visibility`
|
||||
- `sha256`
|
||||
- `storage_status`
|
||||
|
||||
## Job 结果行为变化
|
||||
|
||||
对于新的 `text2image`、`img2img`、`image2video` 结果:
|
||||
|
||||
1. 先从 `PopiNewAPI` 或上游临时结果读出真实内容
|
||||
2. re-host 到 `POPIART_DATA_DIR/media/`
|
||||
3. 把 `local_path + media_id + stable url` 写回 `result_refs_json`
|
||||
|
||||
这样新的 artifact 不再依赖上游临时 URL。
|
||||
|
||||
## 本地 media store
|
||||
|
||||
V1 的本地开发版不引入 S3/R2/OSS,而是用本地文件系统:
|
||||
|
||||
- blob 文件:`POPIART_DATA_DIR/media/blobs/`
|
||||
- metadata JSON:`POPIART_DATA_DIR/media/meta/`
|
||||
|
||||
如果 `POPIART_DATA_DIR` 没显式配置,但 `POPIART_SQLITE_PATH` 已配置,则 `DataDir` 会自动落到 SQLite 所在目录,避免测试把 media 文件写进源码目录。
|
||||
|
||||
## 当前已打通的 URL 复用
|
||||
|
||||
### Artifact / media
|
||||
|
||||
- `media upload` -> stable URL
|
||||
- `artifact upload` -> stable URL
|
||||
- 新 `artifact` 的 `GET /artifacts/:id` -> `url`
|
||||
|
||||
### Runtime output
|
||||
|
||||
- `text2image` 输出会 re-host
|
||||
- `img2img` 输出会 re-host
|
||||
- `image2video` 输出会 re-host
|
||||
|
||||
### URL-first dispatch
|
||||
|
||||
当前已优先 URL 化的路径是:
|
||||
|
||||
- `video.image2video`
|
||||
- 当模型 ID 以 `vidu` 开头,且 reference 已有 stable URL 时,server 会优先用 JSON `images` 传给 `/v1/videos`
|
||||
|
||||
这是为了先覆盖当前测试环境最常用的 `viduq2-pro-fast` 路由。
|
||||
|
||||
## 兼容策略
|
||||
|
||||
旧数据仍然兼容:
|
||||
|
||||
- 如果 `result_ref.kind == data_url`,继续解码读取
|
||||
- 如果 `result_ref.kind == url`,继续代理下载
|
||||
- 如果 `result_ref.kind == local_path`,优先从本地 media store 读取
|
||||
|
||||
也就是说:
|
||||
|
||||
- 历史 artifact 不会立即失效
|
||||
- 新 artifact 才开始享受稳定 URL
|
||||
|
||||
## 后续阶段
|
||||
|
||||
后续如果要走生产化路线,可以保持接口不变,只把存储实现替换成对象存储:
|
||||
|
||||
- 本地 `media/blobs/` -> S3 / R2 / OSS / COS
|
||||
- metadata JSON -> 独立 media table 或对象存储元数据
|
||||
- `GET /v1/media/:id/content` -> CDN / 受控稳定 URL
|
||||
|
||||
但这一步不是 V1 本地开发验证的前置条件。
|
||||
Reference in New Issue
Block a user