Skip to content

上游服务接口设计

概述

统一外部 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()

新增服务步骤

  1. providers/ 下创建文件,实现 SyncProviderAsyncProvider 接口
  2. providers/index.ts 中 import 并添加到 providers 数组
  3. 如需新 ModelType,在 modelTypes.tsapp/shared/registry.ts 中添加
  4. 完成

元数据影响范围

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/create vs /v1/videos
  • 请求格式不同:如 JSON vs multipart/form-data
  • 参数语义冲突:同名参数含义或取值范围不同
  • 渠道特性差异:如官转/逆向渠道有专属参数

当前决策记录

决策说明
video-unifiedNewAPI 视频统一格式,支持 Veo/即梦/Grok Video,端点 /v1/video/create,JSON 请求
openai-videoOpenAI Video 格式,支持 Sora,端点 /v1/videos,multipart/form-data 请求
sora 模型类型同时被 video-unifiedopenai-video 支持,因不同中转站使用不同格式

API 文档参考

详细的上游 API 文档见 docs/internal/api/

视频模型参数差异

视频统一格式(video-unified)支持多个模型,各模型参数存在差异。

参数对比

特性即梦VeoSoraGrok Video
参考图模式底图参考底图/首帧/首尾帧/素材合成底图参考底图参考
参考图数量1 张1-3 张(按子模型)1 张1 张
宽高比参数aspect_ratioaspect_ratioorientationaspect_ratio
宽高比选项16:9, 9:16, 4:3, 3:4, 1:1, 21:916:9, 9:16portrait, landscape2:3, 3:2, 1:1
分辨率参数size-sizesize
分辨率选项720x1280, 1280x720, 1080P-small, large720P
时长参数--duration-
提示词增强-enhance_prompt--
超分辨率-enable_upsample--

Veo 子模型参考图

Veo 有多个子模型,支持不同的参考图模式:

子模型后缀参考图模式最大图片数
无后缀(如 veo2veo3底图参考1 张
-frames(如 veo2-fast-frames首尾帧2 张
-components(如 veo3.1-components素材合成3 张

状态码归一化

各模型返回的状态码不同,Provider 内部归一化为 processingsuccessfailed 三种状态:

状态类型通用状态Veo 特有
处理中pending, processingimage_downloading, video_generating, video_upsampling
成功success, completedvideo_upsampling_completed
失败failedvideo_generation_failed, video_upsampling_failed, error

MJ-Studio - 多模型 AI 工作台