上游服务接口设计
概述
统一外部 AI 服务的接口层,实现"单文件自包含"的 Provider 模式。新增 API 格式只需创建一个 provider 文件并注册,无需修改 task.ts 等调用方。
目录结构
server/services/
├── providers/ # 任务类服务(绘图/视频)
│ ├── types.ts # 接口定义
│ ├── index.ts # 聚合导出 + 工具函数
│ ├── modelTypes.ts # ModelType 注册表
│ ├── dalle.ts # 同步
│ ├── gemini.ts # 同步
│ ├── openaiChatImage.ts # 同步
│ ├── mj.ts # 异步
│ ├── koukoutu.ts # 异步
│ ├── videoUnified.ts # 异步
│ └── openaiVideo.ts # 异步
├── chatProviders/ # 对话类服务
│ ├── types.ts
│ ├── index.ts
│ ├── openaiChat.ts
│ └── claude.ts
└── task.ts # 通过 getProvider() 调度,无 switch
app/shared/
└── registry.ts # 前端注册表(静态导入)核心接口
Provider 元数据
每个 Provider 通过 meta 字段声明元数据:
typescript
interface ProviderMeta {
apiFormat: ApiFormat // API 格式标识
label: string // 显示名称
category: ModelCategory // 'image' | 'video'
isAsync: boolean // 同步/异步模式
supportedModelTypes: (ImageModelType | VideoModelType)[]
capabilities?: ModelCapabilities // 支持的参数能力
validation?: ValidationRules // 验证规则
}同步 Provider
一次调用返回结果(如 DALL-E、Gemini):
typescript
interface SyncProvider {
readonly meta: ProviderMeta & { isAsync: false }
createService(baseUrl: string, apiKey: string): {
generate(params: GenerateParams): Promise<SyncResult>
}
}异步 Provider
提交 + 轮询模式(如 MJ、视频生成):
typescript
interface AsyncProvider {
readonly meta: ProviderMeta & { isAsync: true }
createService(baseUrl: string, apiKey: string): {
submit(params: GenerateParams): Promise<AsyncSubmitResult>
query(upstreamTaskId: string, taskId?: number): Promise<AsyncQueryResult>
}
}使用方式
typescript
// task.ts
const provider = getProvider(task.apiFormat)
const service = provider.createService(baseUrl, apiKey)
if (provider.meta.isAsync) {
await (service as AsyncService).submit(params)
} else {
await (service as SyncService).generate(params)
}
// taskPoller.ts
const ASYNC_API_FORMATS = getAsyncApiFormats()新增服务步骤
- 在
providers/下创建文件,实现SyncProvider或AsyncProvider接口 - 在
providers/index.ts中 import 并添加到providers数组 - 如需新 ModelType,在
modelTypes.ts和app/shared/registry.ts中添加 - 完成
元数据影响范围
Provider 和 ModelType 的元数据影响以下功能:
模型配置页面(上游设置)
- API 请求格式候选项:根据
PROVIDER_REGISTRY生成下拉选项 - 模型类型候选项:根据选中的 apiFormat,从
supportedModelTypes获取
对话页面
- 模型类型显示名称:从
CHAT_MODEL_REGISTRY获取 label - API 格式验证:模型配置的 apiFormat 需在
PROVIDER_REGISTRY中存在
工作台表单
- 参数控件显示:根据
capabilities决定显示哪些参数(垫图、负面提示词、尺寸等) - 默认值:从
ModelTypeMeta.defaults获取默认模型名和预计时间
任务卡片
- 模型标签:从
ModelTypeMeta.cardDisplay获取显示文字和颜色
任务提交验证
- 验证规则:根据
validation检查是否需要提示词、是否必须有图片等
ModelType 注册表
ModelType 与 Provider 是多对多关系,单独管理:
- 后端:
server/services/providers/modelTypes.ts - 前端:
app/shared/registry.ts
包含默认模型名、预计时间、卡片显示配置、参数能力等。
设计决策
实现新的 API 格式或模型类型时,需权衡归并与拆分。
归并原则
以下情况应归并为同一 API 格式/模型类型:
- 端点相同:请求路径、方法、Content-Type 一致
- 参数兼容:核心参数相同,仅部分可选参数有差异
- 响应格式一致:状态码、字段结构可统一处理
拆分原则
以下情况应拆分为不同 API 格式/模型类型:
- 端点不同:如
/v1/video/createvs/v1/videos - 请求格式不同:如 JSON vs multipart/form-data
- 参数语义冲突:同名参数含义或取值范围不同
- 渠道特性差异:如官转/逆向渠道有专属参数
当前决策记录
| 决策 | 说明 |
|---|---|
video-unified | NewAPI 视频统一格式,支持 Veo/即梦/Grok Video,端点 /v1/video/create,JSON 请求 |
openai-video | OpenAI Video 格式,支持 Sora,端点 /v1/videos,multipart/form-data 请求 |
sora 模型类型 | 同时被 video-unified 和 openai-video 支持,因不同中转站使用不同格式 |
API 文档参考
详细的上游 API 文档见 docs/internal/api/。
视频模型参数差异
视频统一格式(video-unified)支持多个模型,各模型参数存在差异。
参数对比
| 特性 | 即梦 | Veo | Sora | Grok Video |
|---|---|---|---|---|
| 参考图模式 | 底图参考 | 底图/首帧/首尾帧/素材合成 | 底图参考 | 底图参考 |
| 参考图数量 | 1 张 | 1-3 张(按子模型) | 1 张 | 1 张 |
| 宽高比参数 | aspect_ratio | aspect_ratio | orientation | aspect_ratio |
| 宽高比选项 | 16:9, 9:16, 4:3, 3:4, 1:1, 21:9 | 16:9, 9:16 | portrait, landscape | 2:3, 3:2, 1:1 |
| 分辨率参数 | size | - | size | size |
| 分辨率选项 | 720x1280, 1280x720, 1080P | - | small, large | 720P |
| 时长参数 | - | - | duration | - |
| 提示词增强 | - | enhance_prompt | - | - |
| 超分辨率 | - | enable_upsample | - | - |
Veo 子模型参考图
Veo 有多个子模型,支持不同的参考图模式:
| 子模型后缀 | 参考图模式 | 最大图片数 |
|---|---|---|
无后缀(如 veo2、veo3) | 底图参考 | 1 张 |
-frames(如 veo2-fast-frames) | 首尾帧 | 2 张 |
-components(如 veo3.1-components) | 素材合成 | 3 张 |
状态码归一化
各模型返回的状态码不同,Provider 内部归一化为 processing、success、failed 三种状态:
| 状态类型 | 通用状态 | Veo 特有 |
|---|---|---|
| 处理中 | pending, processing | image_downloading, video_generating, video_upsampling |
| 成功 | success, completed | video_upsampling_completed |
| 失败 | failed | video_generation_failed, video_upsampling_failed, error |
