Skip to content

feat(providers): 新增 Fal 队列协议面 - #569

Open
johnnyzhang-eng wants to merge 3 commits into
1024XEngineer:mainfrom
johnnyzhang-eng:feat/fal-queue-protocol
Open

feat(providers): 新增 Fal 队列协议面#569
johnnyzhang-eng wants to merge 3 commits into
1024XEngineer:mainfrom
johnnyzhang-eng:feat/fal-queue-protocol

Conversation

@johnnyzhang-eng

Copy link
Copy Markdown
Contributor

Closes #568
#332 第 6.2 节的接口实现第二步。本 PR 叠在 #562 之上(含它的一个 commit),#562 合入后这里只剩两个 commit;不接线、不换主力模型,那是第三步。

三处形态差异走显式表,没有一处靠拼字符串推:鉴权头 Key、首帧字段名按型号族分(fal-ai/kling-video/o1start_image_url,seedance / veo / vidu 是 image_url)、轮询前缀取提交端点的前两段。

一处设计级改动请评审定夺

JobProtocol 加了第六个函数 parse_fetch。原五函数接口没有解析取结果响应的位置,而这一面的成败恰恰只在那一步显形——不加的话第三步接线时仍要改接口。两面都实现了,OpenAI 面行为不变(它的 build_fetchNone,走不到)。

轮询地址选了「按前两段重建」而不是把 URL 带进接口

理由是持久化边界:gateway/models.py:134 持久化的只有 job_id 一列,video.pybound_job_id 是重试循环里的局部变量。一个离开 job_id 就重建不出地址的协议面,日后无法从单号恢复任务。把 URL 带进接口还要动 VideoProvider.follow_job 的签名,而 #332 第 8 节承诺过这两个 Provider 的方法签名不变。

代价是规则错了就是 404 在一条已计费的单据上,用两件事压住:test_rebuilt_poll_url_matches_what_the_gateway_handed_back 拿建单响应里网关自己给的 status_url / response_url 与重建结果逐字节比对;四个端点的实测前缀写成参数化用例。

实测支撑(2026-08-24)

首帧可以直接给 base64 dataURI,不需要上传器。 字段名虽叫 *_url

端点 喂进去的图 产物首帧
fal-ai/kling-video/o1/image-to-video 1280×720 纯品红 平均 RGB (255, 1, 201)
fal-ai/vidu/q1/image-to-video 纯中灰 平均 RGB (130,130,130),标准差 1
fal-ai/veo3.1/image-to-video 纯中灰 中灰方图加黑边,未生成任何内容
bytedance/seedance-2.0/image-to-video 未验:输入尺寸下限 14px,探针图只有 8px 被拒

kling 那条用的是内容对撞:图给纯品红、提示词给 a bird flying,出品红即证明图被采用。

另两条只能靠实测发现的行为(已写进 #332 第 11 节):/status 对成功与失败都返回 HTTP 200 + COMPLETED;取结果时的 HTTP 400 可能表示「还没好」,响应体是 {"status":"IN_PROGRESS"}。按 400 一律判客户端错会把还在跑的任务当失败,而单据已建、可能已计费。

验证

39 条脱网单测。八个变异体全部转红(脚本 + try/finally,还原前后 sha256 一致,且提交后复核该文件 sha256 仍是同一个):

换回什么 转红用例
鉴权头 KeyBearer test_auth_header_is_key_not_bearer
建单路径丢掉 queue/ test_submit_path_keeps_the_queue_prefix_and_leaves_v1_behind
kling-o1 首帧字段改成 image_url test_kling_o1_wants_start_image_url 等 2 条
轮询前缀取三段 test_poll_prefix_is_the_first_two_segments 4 参数 + 2 条
base_url 不退回网关根 test_gateway_root_strips_the_openai_face 4 参数 + 5 条
取结果 400 一律当客户端错 test_fetch_400_in_progress_is_not_ready_rather_than_a_client_error
500 不看 detail test_completed_but_fetch_500_is_a_failure 2 参数
build_fetch 返回 None test_completed_poll_stops_polling_without_claiming_success 等 2 条

本分支验证:ruff check . 通过、lint-imports 2 kept 0 broken、export_openapiopenapi.json 无漂移、pytest -q 1380 passed / 14 skipped(较 #562 的 1341 多 39 条,与新增用例数对得上)。

未覆盖

  • req.seconds 目前落不到请求体上,即时长静默走端点默认值。十个端点的 duration 取值形态分 5 / "5" / "5s" 三种且未逐个实测,猜错就是一次已计费的 400。已用 test_seconds_does_not_reach_the_body_yet 把这个缺口钉成显式契约,第三步接线前必须补测。
  • parse_fetch 对产物地址同时认顶层 video.urlresult.video.url:两次测量在嵌套层级上不一致(08-24 取结果端点是顶层,08-07 /status 内联那份裹在 result 里)。只认一处而对面给的是另一处,丢的是一段已付费的视频,故选择宽认;真实分布待复验。
  • 端点 ↔ 型号的映射未做,adapter 侧尚未调用 build_fetch / parse_fetch

@vercel

vercel Bot commented Aug 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
windup Ignored Ignored Preview Aug 24, 2026 8:37am

@codecov

codecov Bot commented Aug 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.97959% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...indup_framework/providers/protocol/openai_video.py 91.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审阅了固定提交范围 17b25a7...9603b68 的协议、provider 调用链、错误分类和新增测试。整体接口拆分与 FAL/OpenAI 两面建模是清晰的;python3 -m compileall 通过,但当前环境没有安装 pytest/uv/httpx,因此未能执行运行时测试。

Comment thread backend/packages/framework/src/windup_framework/providers/protocol/fal_queue.py Outdated
xyh202131
xyh202131 previously approved these changes Aug 24, 2026
xyh202131
xyh202131 previously approved these changes Aug 24, 2026
@johnnyzhang-eng johnnyzhang-eng added the enhancement New feature or request label Aug 24, 2026
1024XEngineer#332 第 6.2 节的接口写第二个面,只交付协议实现与脱网单测,不接
ALLOWED_VIDEO_MODELS、不换主力模型。

三处与 OpenAI 面不同,均按 2026-08-24 实测:鉴权头是 Key 而不是 Bearer;
建单路径带 queue/ 前缀且与 /v1 平级,故 base_url 要先退回网关根;首帧字段名
按端点族分 start_image_url 与 image_url 两种,表驱动不按字符串猜。

成败不在轮询那一步显形 —— 成功与失败的任务在 /status 都是 200 + COMPLETED,
所以本面的 build_fetch 返回真的 HttpCall,并给 JobProtocol 补上 parse_fetch。
取结果的 400 也不一律是客户端错误:veo3.1 与 vidu 未就绪时返回 400 +
IN_PROGRESS,判成失败会把还在跑、可能已计费的任务判死。

轮询与取结果的地址由建单端点的前两段重建,而不是把 URL 带进接口:ledger 里
只持久化 job_id,一个离开 job_id 就重建不出地址的协议面日后无法从单号恢复。

首帧沿用 OpenAI 面的 JPEG dataURI:四个端点各喂一张纯品红图,产物首帧同色,
故不需要 bytes → 公网 URL 的上传器,sufy.py 里那条相反的注释一并更正。

39 条脱网单测全部以实测响应体为 fixture。8 个变异(鉴权头 / queue 前缀 /
首帧字段名 / 轮询前缀取三段 / base_url 不退根 / 400 当客户端错 / 500 不看
detail / build_fetch 返回 None)逐个确认有用例转红,还原后 sha256 一致。
原注释说四个端点都用纯品红图验过,实际只验了三个、且两个用的是中灰图;
seedance-2.0 因输入尺寸下限 14px 拒了那次探针,没验到。
只挡解码失败不够:上游在 2xx 下返回合法的数组 / 字符串 / null 时,直接 .get()
会抛 AttributeError,请求以未处理异常结束,而单据可能已经建了。两个协议面共用一个校验。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(providers): 新增 Fal 队列协议面

2 participants