feat(providers): 新增 Fal 队列协议面 - #569
Open
johnnyzhang-eng wants to merge 3 commits into
Open
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
xyh202131
previously approved these changes
Aug 24, 2026
johnnyzhang-eng
force-pushed
the
feat/fal-queue-protocol
branch
from
August 24, 2026 04:12
beb35c2 to
dd2d099
Compare
xyh202131
previously approved these changes
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,请求以未处理异常结束,而单据可能已经建了。两个协议面共用一个校验。
johnnyzhang-eng
force-pushed
the
feat/fal-queue-protocol
branch
from
August 24, 2026 08:37
dd2d099 to
78f5024
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #568
按 #332 第 6.2 节的接口实现第二步。本 PR 叠在 #562 之上(含它的一个 commit),#562 合入后这里只剩两个 commit;不接线、不换主力模型,那是第三步。
三处形态差异走显式表,没有一处靠拼字符串推:鉴权头
Key、首帧字段名按型号族分(fal-ai/kling-video/o1是start_image_url,seedance / veo / vidu 是image_url)、轮询前缀取提交端点的前两段。一处设计级改动请评审定夺
JobProtocol加了第六个函数parse_fetch。原五函数接口没有解析取结果响应的位置,而这一面的成败恰恰只在那一步显形——不加的话第三步接线时仍要改接口。两面都实现了,OpenAI 面行为不变(它的build_fetch恒None,走不到)。轮询地址选了「按前两段重建」而不是把 URL 带进接口
理由是持久化边界:
gateway/models.py:134持久化的只有job_id一列,video.py的bound_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-videofal-ai/vidu/q1/image-to-videofal-ai/veo3.1/image-to-videobytedance/seedance-2.0/image-to-videokling 那条用的是内容对撞:图给纯品红、提示词给
a bird flying,出品红即证明图被采用。另两条只能靠实测发现的行为(已写进 #332 第 11 节):
/status对成功与失败都返回 HTTP 200 +COMPLETED;取结果时的 HTTP 400 可能表示「还没好」,响应体是{"status":"IN_PROGRESS"}。按 400 一律判客户端错会把还在跑的任务当失败,而单据已建、可能已计费。验证
39 条脱网单测。八个变异体全部转红(脚本 +
try/finally,还原前后 sha256 一致,且提交后复核该文件 sha256 仍是同一个):Key→Bearertest_auth_header_is_key_not_bearerqueue/test_submit_path_keeps_the_queue_prefix_and_leaves_v1_behindimage_urltest_kling_o1_wants_start_image_url等 2 条test_poll_prefix_is_the_first_two_segments4 参数 + 2 条base_url不退回网关根test_gateway_root_strips_the_openai_face4 参数 + 5 条test_fetch_400_in_progress_is_not_ready_rather_than_a_client_error等detailtest_completed_but_fetch_500_is_a_failure2 参数build_fetch返回Nonetest_completed_poll_stops_polling_without_claiming_success等 2 条本分支验证:
ruff check .通过、lint-imports2 kept 0 broken、export_openapi后openapi.json无漂移、pytest -q1380 passed / 14 skipped(较 #562 的 1341 多 39 条,与新增用例数对得上)。未覆盖
req.seconds目前落不到请求体上,即时长静默走端点默认值。十个端点的duration取值形态分5/"5"/"5s"三种且未逐个实测,猜错就是一次已计费的 400。已用test_seconds_does_not_reach_the_body_yet把这个缺口钉成显式契约,第三步接线前必须补测。parse_fetch对产物地址同时认顶层video.url与result.video.url:两次测量在嵌套层级上不一致(08-24 取结果端点是顶层,08-07/status内联那份裹在result里)。只认一处而对面给的是另一处,丢的是一段已付费的视频,故选择宽认;真实分布待复验。build_fetch/parse_fetch。