OpenAI 兼容止于对话端点:多提供商视频接口分流与真 key 首测连环坑
首次记录:2026-07-20 来源:LibreCanvas v1.4 上线后,用户本地部署首次用真 key 生成视频,一路从 HTTP 404 打到 UI 连环 bug,收口于 v1.5.0。 状态:接口分流与模型实拉已实现并推送;⚠️ 视频生成成功产出未经我方观测(见事实记录)。
事实记录(不可修改区)
- 项目:LibreCanvas(E:\libre-canvas),起点 v1.4.0 → 收口 v1.5.0,commit
940b3ed - 触发:用户本地
npm run dev起站后,用真 key 走视频生成,报HTTP 404: Not Found - 涉及提供商:硅基流动(api.siliconflow.cn)、火山方舟(ark.cn-beijing.volces.com)
- 报错序列(真实发生顺序):
HTTP 404: Not Found—— 打/videos端点,两家都没有视频任务失败:The request failed beca…—— 方舟任务建成功但执行失败,错误文案被 60 字截断- 模型下拉「压缩看不清」「切提供商后选不了」「空盒子」三个 UI 问题
HTTP 400 {"code":20012,"message":"Model does not exist"}—— 硅基流动
- 诚实边界(未验证项):我方全程无可用 key,所有接口行为均据官方文档推断,未做过一次真实调用;最终「修复完成」为用户单方报告,我未观测到任何一次成功的视频产出。硅基流动 20012 的确切成因(image_size 缺失 / T2V 带图 / 账号未开通付费模型)未被隔离验证,三个候选因一次性全修了。
- 数据来源:用户截图、官方文档(docs.siliconflow.cn 三处)、
tsc -b零错误
一句话总结
「OpenAI 兼容」这块招牌只兼容到 /chat/completions——越冷门的模态(视频 > 音频 > 图像 > 文本)各家越是自定义端点、自定义任务流、自定义必填字段;而标识符(模型名、枚举值)一律不许猜,要向接口拉权威列表,连”拉列表”本身都可能带分类过滤,默认返回的不是全集。
弧线速览
- 404:代码只按 OpenAI
/videos规范发请求。硅基流动实际是/video/submit+/video/status(POST 轮询),火山方舟是/contents/generations/tasks(POST 建任务 + GET 轮询)。三家三套,无一相同。 - 任务失败:方舟任务建成功了但执行失败——因为提示词里写着”首帧为图片1""视频1的第一视角""音频1作背景音乐”,而画布根本没有把视频/音频卡传出去的入口(只有图片卡有连线锚点)。Seedance 收到引用了不存在素材的提示词,直接失败。
- UI 连环 bug:为了让用户能选对模型,加了模型下拉,结果一路撞出四个独立 bug(见规律 4/5/6)。
- 模型不存在:我把
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。这不只是显示不对——直接点生成就会拿方舟的模型名去请求硅基流动的接口,报错还很难懂。
判据:凡是”归属于某个上级选项”的字段(模型属于提供商、城市属于省份、分支属于仓库),上级一变,下级必须重置或校验,不能沉默保留。
下次改进
- 最想改的一步:接入新提供商前,先花五分钟读它的接口文档目录结构——本次如果一开始就看到硅基流动文档里视频是独立章节而非
/videos,能省掉整条 404 → 猜模型名 → 删对模型名的弯路。 - 行动清单:
- 把「flex 列容器加
flex:none」「可滚动面板内不用绝对定位浮层」「错误文案不截断」三条塞进前端自查清单; - 新增提供商时,配套补一条”该商特有的必填字段”注释(如硅基流动
image_size); - 补齐硅基流动 20012 的隔离验证——当前三个候选因一起修了,成因未定论。
- 把「flex 列容器加
关联文档
- 2026-07-16_LibreCanvas开源画布_单日十版从立项到v1.4复盘_v1 —— 本次的前作。其”下次改进”预言了真 key 首测的必要性,本次即为应验实录
- ⭐ 交付前实测证伪律_v1 —— 同族纪律:mock 只证接线;本次新增变体”文档也只是二手证据,无 key 时所有接口行为都是未验证假设”
- 复盘事实先行原则 —— 本文遵循:无 key、未观测成功产出,均写进事实冻结区
- Claude预览环境不派发滚动事件_滚动类功能无法行为验证_v1 —— 同为”验证手段够不着”的场景:那次是环境缺陷,这次是缺凭证
- 云端定时内容生产连环坑复盘_全绿不等于已发_v1 —— 同族”全绿≠已达成”:tsc 零错误与真实可用之间隔着一整个真 key 首测