平台工程

OpenAI 兼容止于对话端点:多提供商视频接口分流与真 key 首测连环坑

首次记录:2026-07-20 来源:LibreCanvas v1.4 上线后,用户本地部署首次用真 key 生成视频,一路从 HTTP 404 打到 UI 连环 bug,收口于 v1.5.0。 状态:接口分流与模型实拉已实现并推送;⚠️ 视频生成成功产出未经我方观测(见事实记录)。


事实记录(不可修改区)


一句话总结

「OpenAI 兼容」这块招牌只兼容到 /chat/completions——越冷门的模态(视频 > 音频 > 图像 > 文本)各家越是自定义端点、自定义任务流、自定义必填字段;而标识符(模型名、枚举值)一律不许猜,要向接口拉权威列表,连”拉列表”本身都可能带分类过滤,默认返回的不是全集。


弧线速览

  1. 404:代码只按 OpenAI /videos 规范发请求。硅基流动实际是 /video/submit + /video/status(POST 轮询),火山方舟是 /contents/generations/tasks(POST 建任务 + GET 轮询)。三家三套,无一相同。
  2. 任务失败:方舟任务建成功了但执行失败——因为提示词里写着”首帧为图片1""视频1的第一视角""音频1作背景音乐”,而画布根本没有把视频/音频卡传出去的入口(只有图片卡有连线锚点)。Seedance 收到引用了不存在素材的提示词,直接失败。
  3. UI 连环 bug:为了让用户能选对模型,加了模型下拉,结果一路撞出四个独立 bug(见规律 4/5/6)。
  4. 模型不存在:我把 Wan-AI/Wan2.2-T2V-A14B 猜进预设 → 被判定为幻觉 → 我又误删了它(其实文档里它是对的)→ 查文档才发现真正的问题是:硅基流动 GET /models 默认只返回文本模型,视频模型必须带 ?type=video 才列得出来,所以用户在下拉里永远看不到视频模型。

提炼的规律

1. 「OpenAI 兼容」只兼容到对话端点(⚠️ 首次发现)

提供商宣称”OpenAI 兼容”时,可靠成立的只有 /chat/completions(以及多数情况的 /models/embeddings)。模态越边缘,自定义程度越高

模态兼容度实测
文本/chat/completions 三家一致
图像/images/generations 多数支持,参数有出入
视频硅基 /video/submit、方舟 /contents/generations/tasks、OpenAI /videos,端点、任务流、字段全不同

架构含义:BYOK 类产品不能只写一套 OpenAI 客户端就宣称”接任何兼容接口”。可行解是按 Base URL 做提供商分流(本次实现:正则匹配域名 → 走各家专属函数 → 默认回落 OpenAI 规范),把差异关在一个函数里,调用方无感。

2. 标识符不许猜,要拉权威列表——且默认列表往往不是全集(⚠️ 首次发现)

我把模型名凭印象写进预设,害用户吃了两轮 Model does not exist模型名、枚举值、区域码这类标识符,猜的成本远高于查的成本——猜错不会静默,会变成用户面前的报错。

更隐蔽的第二层:“拉列表”本身可能带默认过滤。硅基流动 GET /models 不带参数时只返回文本模型,视频/图像/音频模型必须 ?type=video|image|audio。所以”我拉了列表还是没有”不等于”账号没有这个模型”,要先确认列表接口的分页/分类语义。

产线化做法:模型选择器直接 GET /models 实拉 + 按「提供商 × 模态」缓存 + 输入框兼作搜索框(模型上百个时必需)。用户永远不该被要求手记模型名。

3. 真 key 首测是一道独立工序,不是”验收的一部分”(⭐ 强再验)

2026-07-16_LibreCanvas开源画布_单日十版从立项到v1.4复盘_v1 的「下次改进」第一条写的就是:“尽早让用户配一个真 key 做一次真实生成——mock 验证只能证明’接线对’“。这次本地部署就是那次首测,一次炸出 8 个问题(3 个接口层、4 个 UI 层、1 个交互缺口),全部是 mock 和 tsc 看不见的。

预言应验的意义不在于”我早说了”,而在于确认了真 key 首测该被当成一道独立工序排进计划,而不是指望它在别的验收里顺带完成。它验的东西是别的手段结构性看不见的:端点存在性、必填字段、模型标识符、错误文案可读性、账号权限。

同族纪律见 ⭐ 交付前实测证伪律_v1

4. flex 列容器会压缩子项,不会自动滚动(前端通用坑,⚠️ 首次发现)

display:flex; flex-direction:column + max-height + overflow-y:auto 的下拉菜单,装进 100+ 项时不滚动,而是把每一项等比压扁——因为 flex 子项默认 flex-shrink: 1。表现是用户截图里的”每行被挤成一条缝,完全看不清”。

解法固定:菜单项加 flex: none + min-height。凡是”flex 列 + 可滚动 + 项数不定”的组合都要预防性加,别等项数上来才发现。

5. 绝对定位浮层会被祖先的 overflow 裁切(前端通用坑)

同一个下拉菜单还撞了第二个坑:它绝对定位浮在输入框下方,而外层面板有 overflow-y: auto。绝对定位元素撑不开滚动容器的内容高度,超出部分被直接裁掉,只剩一条缝加个滚动条。

两个解法:① 改成随内容排布的展开列表(本次选择,简单可靠,展开时把下方内容推开);② portal 到 body 用 fixed 定位(跟随滚动要额外处理)。在可滚动面板里做浮层,默认选 ①。

6. 报错截断会掩盖唯一的诊断信息(⚠️ 首次发现)

方舟那次失败,节点上只显示 视频任务失败:The request failed beca…——为了排版把错误截到 60 字,而关键原因恰好在被截掉的部分。排错时错误文案是唯一线索,截断它等于自断诊断能力。

判据:状态类文案可以截断,错误文案不可以。至少要有一处(面板/展开区/控制台)能看到全文。本次改为节点上换行显示两行 + 面板内始终全文。

7. 跨提供商状态残留:切了 A 家还带着 B 家的模型名(⚠️ 首次发现)

用户从方舟切回硅基流动,模型输入框里还留着 doubao-seedance-2-0-260128。这不只是显示不对——直接点生成就会拿方舟的模型名去请求硅基流动的接口,报错还很难懂。

判据:凡是”归属于某个上级选项”的字段(模型属于提供商、城市属于省份、分支属于仓库),上级一变,下级必须重置或校验,不能沉默保留。


下次改进


关联文档

类型/平台工程主题/API适配来源/LibreCanvas