Files
Genarrative/docs/【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md
T
suzmii aa8e3507d1
Project CI / Repository checks (push) Successful in 4m20s
Project CI / Frontend tests (push) Successful in 4m31s
Project CI / Backend tests (push) Successful in 5m45s
Project CI / Native shell tests (push) Successful in 14m53s
新增 External v1 去背景生成链路 (#184)
新增外部去背景 API、MCP 工具与异步队列契约

补齐来源归属、媒体类型、幂等重放和画布原子持久化校验

修复 provenance 重建、assetKindOverride 门禁与 revision retry 竞态

同步 Python helper、Skill、OpenAPI 及项目文档

---------

Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/184
Co-authored-by: suzmii <suzmii@foxmail.com>
Co-committed-by: suzmii <suzmii@foxmail.com>
2026-08-24 14:37:32 +08:00

31 KiB
Raw Blame History

外部 OpenAPI 与 API Key 接入方案

背景

外部调用方需要通过稳定 HTTP 契约或托管式远程 MCP 使用图片画布编辑器内的素材生成、编辑和管理能力,并能创建项目、保存画板布局和管理账号级素材库。不支持 MCP 的 Agent 还需要可发现、可校验、可完整下载的 Skill 包,而不是只有一份 OpenAPI JSON。全部入口必须走 server-rs + Axum + SpacetimeDB 正式链路,不能把 API Key、画板状态、素材状态、生成任务或生成结果放到前端临时状态中。

v1 范围

本期新增外部 API 命名空间:

/api/external/v1

v1 只开放以下能力:

  • POST /api/external/v1/assets/direct-upload-tickets:创建素材直传 OSS 凭证。
  • POST /api/external/v1/assets/objects/confirm:确认已上传素材对象,ownerUserId 固定为 API Key 所属账号。
  • GET /api/external/v1/assets/read-url:获取私有素材读取签名 URL。
  • GET /api/external/v1/editor/projects:列出当前 API Key 所属账号的图片画布项目;view=full|summaryREST 默认 fullMCP 固定使用 summary
  • POST /api/external/v1/editor/projects:创建图片画布项目。
  • GET /api/external/v1/editor/projects/recent:读取当前账号最近图片画布项目。
  • GET /api/external/v1/editor/projects/{projectId}:读取项目与默认画布。
  • PATCH /api/external/v1/editor/projects/{projectId}/metadata:更新图片画布项目标题。
  • DELETE /api/external/v1/editor/projects/{projectId}:删除图片画布项目,并级联清理默认画布和项目资源元数据。
  • PATCH /api/external/v1/editor/projects/{projectId}/canvas:保存默认画布的 viewport 和 layers。
  • POST /api/external/v1/editor/projects/{projectId}/resources:创建项目画布资源记录。
  • GET /api/external/v1/editor/assets/library:读取账号级编辑器素材库。
  • POST /api/external/v1/editor/assets/folders:创建素材文件夹。
  • PATCH /api/external/v1/editor/assets/folders/{folderId}:更新素材文件夹名称或折叠状态。
  • DELETE /api/external/v1/editor/assets/folders/{folderId}:删除素材文件夹并返回最新素材库。
  • POST /api/external/v1/editor/assets:创建素材记录。
  • PATCH /api/external/v1/editor/assets/{assetId}:更新素材名称或所在文件夹。
  • DELETE /api/external/v1/editor/assets/{assetId}:删除素材记录。
  • POST /api/external/v1/editor/images/generations:异步提交编辑器图片素材生成;通过 kind 支持普通图、规范图 spec、角色图 character、快速编辑参考图 quick-edit、UI 设计图 ui-design 和宣发素材 publication-material。External v1 当前不开放结构化游戏场景生成,kind = sceneassetKind = scene 均在入队前返回 400
  • POST /api/external/v1/editor/images/edits:异步提交已有图片重绘 / 调整。
  • POST /api/external/v1/editor/images/background-removals:异步提交已有静态图片去背景;只接受当前账号拥有的稳定 objectKey、项目资源 ID 或素材 ID。可选 assetKind 必须与权威来源类型一致,视频、音频、动画和图片序列类型在入队前返回 400;拒绝 taskId 与其它未声明字段。
  • POST /api/external/v1/editor/icon-spritesheets/generations:异步提交规范图驱动的图标 spritesheet 生成和拆分。
  • POST /api/external/v1/editor/ui-designs/assets/extractions:异步提交 UI 设计图素材提取 / 拆分。
  • POST /api/external/v1/editor/character-animations/generations:异步提交角色动画预览和帧序列生成。
  • POST /api/external/v1/editor/videos/generations:异步提交编辑器视频生成,支持现有 Seedance / Kling / Veo 模型参数和参考媒体限制。
  • POST /api/external/v1/editor/audios/sound-effects/generations:异步提交编辑器音效生成。
  • POST /api/external/v1/editor/audios/background-music/generations:异步提交编辑器背景音乐生成。
  • GET /api/external/v1/generations/{operationId}:按 API Key owner 查询异步生成状态;completed 时返回 compact 稳定结果引用,跨 owner 按不存在处理。
  • GET /api/external/v1/openapi.json:导出本版本 OpenAPI 3.1 JSON。
  • GET /api/external/v1/agent-integration.json:公开导出 Agent 集成发现 manifest,声明 MCP、OpenAPI、Skill 入口、完整 Skill archive、archive SHA-256 和包内文件清单。
  • GET /api/external/v1/skill/SKILL.md:公开读取 Skill 原始入口。
  • GET /api/external/v1/skill.zip:公开下载完整 Skill 包。
  • POST /api/external/v1/mcp:使用相同 Bearer API Key 的托管式 Streamable HTTP MCP;对外暴露本节 OpenAPI operation tools 以及使用说明、OpenAPI、Skill 入口 SKILL.md 和逐个 Skill reference 文档,不开放内部 SpacetimeDB MCP。

九类生成 POST 全部要求 Idempotency-Key,成功只返回 HTTP 202 AcceptedoperationIdkindstatusstatusUrlpollAfterMsupdatedAtMicros。调用方不得把 202 当作媒体生成完成,也不得在网络结果不确定时换一个幂等键重新提交。

图片生成、图标 spritesheet 和 UI 素材提取的 completed compact result 可携带可选结构化 warning { code, reason };任务查询顶层 warning 是可直接展示的有界摘要。外部 OpenAPI 当前公开四个稳定 code

  • postprocess-failed-source-preserved:生成成功,但透明处理、像素规整等后处理未完成,接口保留仍可使用的原图或进入该步骤前的结果。
  • dimension-restore-fallback:该告警只描述进入可选 pixelArt 处理前的交付尺寸归一结果;无法安全归一时,在该处理边界保留 provider 回图尺寸。它不描述或约束 pixelArt 成功后的最终尺寸;若随后像素规整成功,最终产物是整数倍放大后的 PNG,不能据此推断最终宽高等于 provider 回图。
  • unsupported-image-style:请求的图片后处理风格未知或不适用于当前生成类型,接口按无风格继续生成。
  • multiple-generation-warnings:同一成功响应合并了不同 code 的多条非阻断告警,具体原因按顺序拼接在 reason

手工调用 POST /api/external/v1/editor/assetsPOST /api/external/v1/editor/projects/{projectId}/resources 创建 assetKind=character-animation 记录时,generationInputs 只保存可重放的生成输入,不接受 characterAnimationframespreviewVideoPathframeCountfpsdurationSeconds 等旧运行字段;完整正式帧序列和总时长必须分别写入 imageSequenceFramesimageSequenceDurationMsscreenColorHexmattingProvidermattingModel 属于内部处理审计字段,External v1 会在持久化前移除。角色动作生成接口已经直接返回正式 resource / asset,正常调用方不应再手工复制第一帧创建重复记录。

provider 原图已保存但透明背景处理最终失败时,worker 保留原图稳定引用,不返回不存在的透明处理图,图标和 UI 也不继续拆分;有 projectId + canvasCompletion 时由原图完成画布写回。调用方应展示告警,但不得把 completed 任务改判为失败。该降级只覆盖透明背景处理的最终失败,phase 上报、原图或透明处理图持久化、画布写回失败仍使任务失败。图标 / UI 已成功生成透明图、只有自动拆分失败时继续使用既有 sliceWarning。通用 warningsliceWarning 只在「透明背景最终失败」这一条上互斥;风格归一化或像素规整产生的通用 warning 可以与 sliceWarning 并存,compact result 不得丢弃任一条。

异步提交、查询与幂等

外部生成不受 GENARRATIVE_EXTERNAL_GENERATION_MODE=inline 影响:无论站内本地排障模式如何配置,External v1 都只持久化入队并返回 202,不在 API 请求中同步执行 provider。正式状态源是既有 external_generation_job;生成核心、计费、OSS、画布写回、lease 续租和 fencing 继续由现役 worker 链路负责。

调用规则:

  1. 调用方为一次逻辑生成分配 1-128 字节、无空格的可打印 ASCII Idempotency-Key
  2. 服务端以 owner、job kind、幂等键和规范请求建立稳定去重身份;同一请求的传输重试必须复用原键。
  3. 202 响应通过 Location / statusUrl 指向 /api/external/v1/generations/{operationId},并提供 Retry-After / pollAfterMs
  4. queued/running 返回 phase、进度与下一次建议轮询间隔;completed 返回 resultfailed 返回脱敏 error。调用方自己的轮询超时不改变任务状态。
  5. result 只保留稳定 objectKeyresourceIdassetIdassetObjectId、尺寸、媒体类型、taskId、warning 等轻量引用;禁止持久化完整 project/canvas、大型布局快照、Data URL、Blob URL、过期 signed URL、worker lease/fencing 字段和内部 provider 诊断。
  6. 需要完整项目或素材库状态时,调用方在 completed 后重新读取项目或素材库;需要下载媒体时,用稳定 objectKey/assets/read-url 获取短期签名 URL。

结果查询使用 API Key owner 过滤。任务不存在、已删除或属于其他 owner 时统一返回 404,不能通过差异错误枚举他人 operationId。

托管远程 MCP

/api/external/v1/mcp 是 Genarrative 托管的远程端点,Agent 只需配置 URL 和现有 API Key,不安装本地 MCP server。首版兼容 MCP 2025-11-25 initialize 生命周期,使用 JSON-RPC 2.0 和 Streamable HTTP,支持 initializenotifications/initializedpingtools/listtools/callresources/listresources/read。服务端使用无协议 session 的 JSON direct 模式,不依赖 sticky session,也不把 Mcp-Session-Id 作为业务身份。

MCP transport 的 DNS rebinding 防护必须同时允许正式入口 www.genarrative.world / genarrative.world、开发入口 dev.genarrative.world 和本机开发入口;对应 HTTPS Origin 也必须与公开环境同步登记。新增公开环境域名时,必须在发布前使用该域名的真实 HostOrigin 执行 initialize 回归,不能只用 localhost 单测证明端点可用。

MCP tools 从同一份 OpenAPI operation 自动形成 snake_case 名称,并在进程内复用 External REST router,因此鉴权、scope、owner、入参、幂等、计费和结果查询契约只有一份。MCP bridge 从 operation 或 path 的 required Idempotency-Key header 参数自动推导 idempotencyKey 工具参数和转发头,不维护独立的生成 operation 白名单;因此新增异步生成 operation 时,OpenAPI 契约本身就是 MCP 幂等注册来源。MCP transport 的 Authorization 头不能代替逐次业务幂等键。工具结果使用 structuredContent;业务失败使用 isError=true 的结构化安全错误,协议不可路由时才返回 JSON-RPC error。

list_editor_projects 是项目选择工具,服务端固定以 view=summary 调用项目列表,不允许因 OpenAPI 的 REST 默认值退回完整视图。摘要逐项目只返回 projectIdtitleupdatedAt 和可空 cover,不携带 canvasviewportlayersresources 或图片正文;选定目标后再用 get_editor_project 读取完整权威状态。cover 只包含最新项目封面快照的 resourceId、稳定 objectKey、尺寸与 updatedAt,没有封面时为 null。需要展示封面时,以 objectKey 调用 /api/external/v1/assets/read-url 获取短期签名 URL;列表不得内嵌 Data URL、图片二进制或临时签名 URL,也不得把签名 URL 当作持久引用。

MCP 暴露下列稳定文本资源:

  • genarrative://external-editor/usage:关键工作流和异步轮询规则。
  • genarrative://external-editor/openapi:完整 External v1 OpenAPI。
  • genarrative://external-editor/skill:Skill 入口原文;保留首版已声明的稳定 URI。
  • genarrative://external-editor/skill/references/capability-routing.md:能力选路与场景边界。
  • genarrative://external-editor/skill/references/api-operations.md:公开 API 操作、必填字段与调用顺序。
  • genarrative://external-editor/skill/references/authentication-and-safety.md:API Key 鉴权、幂等与安全边界。
  • genarrative://external-editor/skill/references/requests-and-outputs.md:异步提交、状态轮询与 compact 结果语义。

Skill 日后新增 references/ 文档时,MCP 必须按包内相对路径逐个增加 genarrative://external-editor/skill/references/<name> resource,不得只暴露 SKILL.md 而让 Agent 无法读取其引用。当前稳定 reference 精确为上述四篇,不得声明不存在的 reference。MCP Agent 直接调用托管 tools,不下载或安装 Python CLIscripts/tests/.github/workflows/ 不作为 MCP resources。

MCP 必须始终复用 require_external_api_keyowner 从 ExternalApiPrincipal 获取,不接受请求参数伪造 owner。禁止透传内部 external_generation_job procedure、worker controller、SpacetimeDB MCP 或 lease/fencing 控制面。

MCP 缺少、格式错误或无法验证 Bearer API Key 时仍返回 HTTP 401,但不能只返回通用“未授权访问”。响应必须附带 WWW-Authenticate: Bearer realm="genarrative-external-editor",并在安全 JSON details.guide 中给出稳定 reason=MCP_AUTHENTICATION_REQUIREDaction=CONFIGURE_BEARER_API_KEYAuthorization: Bearer <tnr_sk_...> 格式、登录后前往「开发者 API Key」创建密钥、原始密钥只显示一次、不得粘贴到聊天或写入仓库、配置后重试 initialize 的结构化信息,以及公开 manifest、Skill 入口和 OpenAPI 地址。三种失败使用同一响应,不得通过文案或结构差异枚举 Key 是否存在;引导不得匿名暴露 tools、resources 或 owner 信息。agent-integration.jsonmcp.credentialSetup 同步提供 action、Header 值格式、导航标签和公开 Skill 引导地址,不编造未纳入公开契约的账户页面 URL。

Agent 集成发现与完整 Skill 包

agent-integration.json 是机器可读的统一发现入口。支持远程 MCP 的 Agent 读取其中 mcp.transport/url/authentication,通过 MCP resources 读取 Skill 入口和所需 references,直接调用 MCP tools,不安装 CLI。仅不支持 MCP,或需要在 Agent 所在机器上编排本地文件上传的调用方下载 skill.archive,核对 archiveSha256,解压后从 genarrative-external-editor-api/SKILL.md 进入。

Skill archive 必须至少包含:

  • SKILL.md
  • references/capability-routing.md
  • references/api-operations.md
  • references/authentication-and-safety.md
  • references/requests-and-outputs.md
  • scripts/genarrative_external_api.py
  • agents/openai.yaml

包由 api-server 直接从仓库同源文件构建,不能只返回光秃秃的 OpenAPI JSON,也不能把个人 API Key、环境配置或本机路径写入包。完整 skill.zip 只服务不支持 MCP 或需要本地文件编排的 Agent,不是 MCP resource catalog 的压缩包镜像。Python helper 对上层保持便利的同步函数外观,但内部必须执行“异步提交 → 保存 operationId → 按 pollAfterMs 查询 → completed 返回 result”,查询超时应保留 operationId 供后续继续,不得换键重提。

api-server 使用 include_str! 嵌入 OpenAPI 与 Skill 源文件;容器构建阶段必须同时复制 docs/openapi/.codex/skills/genarrative-external-editor-api/,不能只复制 server-rs/,否则本地 Cargo 验证虽可通过,隔离镜像构建会在编译期找不到同源资源。

管理 API Key 的登录态接口保留在站内个人中心链路,但不写入外部 OpenAPI JSON

GET    /api/profile/api-keys
POST   /api/profile/api-keys
DELETE /api/profile/api-keys/{keyId}

前端入口位于登录后个人中心的 我的 → 开发者 API Key,用于查看当前 Key、创建新 Key、复制一次性明文和撤销已创建 Key。Key 卡片中的创建时间和最近使用时间必须格式化为 YYYY-MM-DD,不得直接展示后端时间戳。外部 OpenAPI 只描述 /api/external/v1 下可由 API Key 调用的接口,不混入登录态 API Key 管理接口。

版本与兼容策略

当前状态:v1 尚无外部存量调用方

截至 2026-08-08,已按当前线上 API Key 与调用方状态再次确认没有外部第三方存量调用方,v1 仍处于「已发布但无存量集成」阶段。本节记录的豁免只在该前提成立时有效;本次确认不自动延续到今后的 breaking change,每次仍需重新取得当日线上状态并形成明确决策。

已接受的未版本化 breaking change

2026-07-31「收紧编辑器内部处理元数据边界」从下列响应中移除了原本 requiredproviderinfo.version 保持 1.0.0,路径前缀保持 /api/external/v1

  • EditorImageGenerationResponseEditorIconSpritesheetGenerationResponseEditorVideoGenerationResponseEditorAudioGenerationResponseprovider 原为 required 且非空 string,属性整体移除,JSON key 不再出现。
  • EditorProjectResourceEditorAssetprovider 原为可选 nullable,属性移除后同样不再出现在响应中。

受影响端点为 POST /api/external/v1/editor/images/generations.../images/edits.../icon-spritesheets/generations.../ui-designs/assets/extractions.../videos/generations.../audios/sound-effects/generations.../audios/background-music/generations,以及全部引用上述两个资源 schema 的读取端点。

这是 breaking change,不是文档同步:严格反序列化的调用方(OpenAPI Generator 生成的 Java / Kotlin / C#、pydantic、serde 非 Option 字段)在 required 字段缺失时直接失败,且失败发生在服务端上线瞬间,不需要调用方做任何动作。脱敏目标本身成立,接受不升版本、不设弃用期的唯一依据是当前无存量调用方。

2026-07-31 同一豁免还覆盖了「八类生成从同步成功响应切换为 202 + operationId,新增统一查询接口」这一 breaking change。旧调用方若仍把生成 POST 响应当作媒体结果会立即失败;接受原地修改 v1 的唯一依据同样是上线前已确认没有外部第三方存量调用方。托管 MCP、集成 manifest 与 Skill archive 均为新增入口,不产生既有客户端兼容债务。

2026-08-08「收紧图片编辑主来源契约」把 POST /api/external/v1/editor/images/edits 的既有必填 sourceImageSrc 替换为新的必填 sourceReferenceId,并移除可选 sourceResourceId / assetKindinfo.version 继续为 1.0.0,路径继续为 /api/external/v1。严格客户端会因必填字段改名、旧字段被 additionalProperties: false 拒绝而立即失败,这属于本节定义的 breaking change。产品负责人已于 2026-08-08 根据当前线上 API Key 与调用方状态确认仍无外部调用方,因此明确接受本次不增加兼容字段、不新开 /v2、不设弃用期的原地变更。该豁免只覆盖本次字段替换,不得被后续 breaking change 自动引用。

豁免的失效条件

API Key 由用户在个人中心自助发放,因此「无外部调用方」不是受控状态,可能在无人决策的情况下变为假。本节豁免在下列任一条件出现后立即失效:

  • external_api_key 出现属于非内部账号的活跃密钥;
  • 对外公布 v1 契约、接入文档或示例代码;
  • 与外部团队开始基于 v1 的联调。

正式对外发放第一个外部密钥之前,必须先确认下节规则已生效且 v1 契约已冻结。

今后的变更规则

下列改动视为 breaking,不得在同一 info.version 与同一路径前缀下直接发布:

  • 删除响应字段,或把响应字段移出 required
  • 收窄字段类型、取值枚举或长度约束;
  • 在不改字段名的前提下改变字段语义;
  • 新增请求必填字段,或收紧既有请求字段的校验。

存量调用方出现后,上述改动按以下顺序择一处理:保留字段并返回兼容值(可为 null 或占位值);标注 deprecated 并公告弃用期后再移除;或开设 /api/external/v2 并冻结 v1。仅更新 docs/openapi/genarrative-external-v1.openapi.json 不构成合规的变更流程。

鉴权

外部调用使用 Bearer API Key

Authorization: Bearer tnr_sk_xxx

规则:

  • API Key 归属于 owner_user_id,外部接口只能访问该账号自己的项目、画布和生成素材。
  • 明文 Key 只在创建接口返回一次,后端只保存 key_hashkey_prefix
  • API Key 被撤销后立即不可再用于外部接口。
  • 外部 API 鉴权不复用登录态 JWT,不检查 refresh session;它是独立开发者凭据。
  • OpenAPI JSON、Agent 集成 manifest、原始 Skill 入口和完整 Skill archive 公共可读,不需要鉴权;MCP 与全部业务操作需要鉴权。

数据模型

新增 SpacetimeDB private 表:

external_api_key

字段:

  • key_id:主键。
  • owner_user_id:所属账号。
  • name:用户可识别名称。
  • key_prefix:前缀片段,用于列表展示和排障。
  • key_hash:完整 Key 的 SHA-256 十六进制摘要,唯一。
  • scopes_json:作用域 JSONv1 固定包含 editor:projecteditor:canvaseditor:image-generateeditor:asset。其中 editor:image-generate 覆盖图片生成、重绘、去背景、规范图、宣发图、图标拆分、UI 素材拆分、角色动画、视频、音效和音乐生成。
  • created_at / last_used_at / revoked_at / updated_at

SpacetimeDB procedure

  • create_external_api_key_and_return
  • list_external_api_keys_and_return
  • revoke_external_api_key_and_return
  • authenticate_external_api_key_and_return

素材生成与落库

外部生成接口复用站内编辑器已有 DTO、入队器和 worker executor,不维护第二套生成语义:

  • 图片生成 / 重绘 / 去背景 / 规范图 / 宣发图 / UI 设计图复用站内编辑器的校验、模型归一、计费和持久化规则,但 External handler 固定只入队。External v1 图片修改必须提交当前账号已登记的项目资源 ID 或素材 ID 作为 sourceReferenceId;上传对象必须先登记为项目资源或素材。objectKey、URL、Data URL、Blob URL 以及旧 sourceImageSrc/sourceResourceId/assetKind 字段均返回 400。服务端分别按资源 ID 与素材 ID 主键窄查,双表冲突、未命中、越权或对象记录无效均失败关闭,快速编辑的完整有效类型白名单为 null / spec / character / icon-spritesheet / icon-spec / publication-material / ui-design / sceneOpenAPI 的 x-genarrative-allowed-effective-asset-kinds 必须与后端白名单精确一致。请求带 targetLayerId 时必须同时带 projectId;目标图层必须关联有效项目资源,来源与目标优先比较 assetObjectId,缺失时比较 canonical (bucket, objectKey),来源默认类型还必须与目标资源默认类型一致。入队载荷保存版本化权威快照,worker 执行前再次定点解析并拒绝身份或类型漂移;仅以素材 ID 编辑时不伪造项目资源关系。主站和 External 的通用图片入口共用场景专用合同边界校验,禁止用 kind = sceneassetKind = scene 绕过后端场景 Prompt 组装;场景专用 handler 自己构造规范请求,不受该通用入口校验影响。External v1 去背景接受稳定 objectKey、项目资源 ID 或素材 ID,入队前按当前 owner 解析并规范化为权威 objectKey,同时从项目资源或素材库读取权威语义类型,并只把资产对象存储类型用于非静态媒体门禁;显式项目资源 ID / 素材 ID 优先于 objectKey 匹配,纯 objectKey 对应多条且权威元数据不一致时返回 400 并要求 sourceResourceId 或业务 ID 消歧,禁止依赖项目列表顺序。请求 assetKind 与权威语义类型冲突,或任一记录表示视频、音频、动画、图片序列时返回 400,最终队列载荷只保存服务端解析出的静态语义类型。提供 targetLayerId 时始终必须同时提供 projectId;存在 canvasCompletion 时按生成完成链路写入画布,targetLayerId 不参与原位替换;没有 canvasCompletion 时,目标图层必须存在并关联当前项目静态图片资源,并与来源优先按 assetObjectId、缺失时按 canonical (bucket, objectKey) 证明为同一对象,默认权威类型也必须一致,否则在入队前返回 400;纯 objectKey 省略 sourceResourceId 时自动绑定目标图层资源,并把绑定写入队列供 Worker 再验证。两种画布字段都未提供时只持久化请求指定的项目资源或素材记录,不自动写入画布。URL、未登记或越权引用、无效目标均在入队前失败。外部 DTO 不暴露内部 taskId,也不接受 OpenAPI 未声明字段;任务 ID 只能由服务端队列生成。
  • 图标 spritesheet 和 UI 设计图素材提取复用站内拆分逻辑,生成图集后按连通域切片,并把图集与切片都按请求写入项目资源和素材库。
  • 角色动画、视频、音效和背景音乐复用站内编辑器生成链路;请求携带 assetFolderId 时按站内规则写入素材库,音频类外部调用使用 API Key 所属账号作为 asset owner。
  • API Key 管理接口仍只属于登录态个人中心,不进入外部 OpenAPI JSON。

素材外部生成由 worker 成功后:

  1. 通过 OSS / asset object adapter 持久化媒体文件。
  2. 写入 editor_asset,让生成素材进入账号级素材库。
  3. 如果请求带 projectId,写入 editor_project_resource
  4. result_payload_json 保存 compact 稳定引用,由查询接口返回素材 ID、资源 ID、objectKey、尺寸、媒体类型、model、taskId 和 warning;普通 External API 响应、项目资源与素材 read model 不返回生成 provider、原始 prompt 或内部抠图审计字段。同源画布前端自动提交默认 segModel 属于站内 BFF 请求契约,不因此向 External OpenAPI 开放该字段。

如果请求未带 projectId,只生成并写入素材库;调用方可随后创建项目或自行保存画板布局。

素材与项目资源操作

外部素材操作复用站内图片画布素材库和项目资源的后端事实源:

  • 外部上传素材时先调用 POST /api/external/v1/assets/direct-upload-tickets 获取 OSS 表单直传参数,上传完成后调用 POST /api/external/v1/assets/objects/confirm 写入 asset_object,再用 assetObjectId / objectKey 创建账号级素材或项目资源记录。
  • 外部读取私有 generated / uploaded 素材预览时调用 GET /api/external/v1/assets/read-url 获取短期签名 URL;不开放浏览器 STS 写权限。
  • 素材库读取、文件夹创建 / 更新 / 删除、素材创建 / 更新 / 删除、项目画布资源创建全部通过 api-server -> spacetime-client -> spacetime-module
  • 所有外部素材接口使用 API Key 所属的 owner_user_id,不能由请求体传入 owner。
  • 素材库仍是账号级事实源,不归属于单个项目;项目画布内资源继续使用 editor_project_resource
  • 素材创建和项目资源创建接口只保存记录和元数据;图片二进制上传、签名读取和生成图持久化继续复用已有资产 / 生成链路。

OpenAPI 导出

OpenAPI 3.1 JSON 固定落在:

docs/openapi/genarrative-external-v1.openapi.json

服务端 GET /api/external/v1/openapi.json 和 MCP OpenAPI resource 使用同一份 JSON,通过 include_str! 导出,避免运行时生成结果与仓库文档漂移。MCP tool catalog 同样以这份 OpenAPI 的 path、method、operationId、参数和 request body 是否存在为来源;字段级精确约束继续以 OpenAPI resource 为准。

验收

  • API Key 创建只返回一次明文,列表不返回明文。
  • 撤销后的 API Key 调用外部接口返回 401
  • 九类外部生成 POST 缺少或携带非法 Idempotency-Key 时返回 400;同一 owner、请求和 key 重试只得到同一 operation。
  • External 通用图片生成携带 kind = sceneassetKind = scene 时均返回 400,且不得产生入队尝试。
  • 九类外部生成 POST 固定返回 202,查询能从 queued/running 收敛到 completed/failed;调用方超时后使用原 operationId 继续查询。
  • 外部图片生成、重绘、去背景、图标拆分、UI 素材拆分、角色动画、视频、音效和音乐 completed 后,生成结果按请求同时出现在画布资源和账号级素材库。
  • 角色图、图标 spritesheet 和 UI 素材提取的 completed result 允许携带 EditorGenerationWarning;provider 原图保留降级与自动拆分降级必须保持成功状态,并分别使用通用 warning 与兼容 sliceWarning 表达。
  • 外部视频、角色动画、音效和音乐接口使用站内编辑器相同的请求校验、模型限制和价格校验。
  • OpenAPI JSON 能被 serde_json 解析,且 security scheme 为 Bearer API Key。
  • 项目列表 REST 默认 view=full 并保持完整响应兼容;view=summary 只返回项目选择元数据和可空封面稳定引用,MCP list_editor_projects 固定使用该摘要视图,不因完整项目数据量增长触发返回体上限。
  • 摘要封面不内嵌图片或签名 URL;使用 cover.objectKey/assets/read-url 后才能临时展示。
  • OpenAPI JSON 不包含 /api/profile/api-keysUserAccessToken 或 API Key 管理 schema。
  • agent-integration.json 能发现 MCP、OpenAPI、Skill entry/archive;下载 archive 的 SHA-256 与 manifest 一致,ZIP 包含 SKILL.md、四篇 references、Python helper 和 agents/openai.yaml 七个声明文件且不含凭据。
  • MCP 在无 Bearer、Bearer 格式错误或 Key 无效时返回相同的 401 + WWW-Authenticate + details.guide 鉴权引导,且不暴露 tools/resources/owner;合法 Key 可完成 initialize、tools/list、resources/list/read 和生成提交/查询;resource catalog 必须包含 usage、OpenAPI、skill 主入口和当前全部 Skill references,当前精确为 skill/references/capability-routing.mdskill/references/api-operations.mdskill/references/authentication-and-safety.mdskill/references/requests-and-outputs.md,且不包含 CLI 脚本、测试或 workflow;多实例不依赖 sticky session,不暴露内部 SpacetimeDB MCP 或 worker 控制面。
  • 外部素材库接口覆盖当前已有素材操作:直传凭证、素材对象确认、签名读取、读取素材库、创建 / 更新 / 删除文件夹、创建 / 更新 / 删除素材、创建项目画布资源。
  • 外部项目接口覆盖当前已有项目管理操作:项目列表、最近项目、创建、读取、重命名、删除和默认画布保存。
  • 外部素材生成接口覆盖当前已有编辑器素材操作:生图、重绘 / 调整、规范图生成、宣发素材生成、图标素材生成与拆分、UI 设计图生成与拆分、角色动画、视频、音效和背景音乐。
  • 修改 SpacetimeDB schema 后运行 npm run spacetime:generatenpm run check:spacetime-schema