Repository navigation
fix(wechat): 视频号瞬态失败有限重试 + attempts 带脱敏失败原因(#34 #33) - #37
Merged
Merged
Conversation
…easons _run_chain retries retryable per endpoint (wechat: 3 attempts + 0.3s backoff, others: 1 = unchanged). classify may return (decision, reason); _call_endpoint raises EndpointHttpError(status, body) on wechat POST non-2xx dict bodies so attempts records error_body + http_status. fetch_wechat_channels_media drops its hand-rolled loop (was 3x3=9). Task-Id: Fixes-Issue: #34, #33 Agent-Executor: ocgo-muse-pi Agent-Model: muse-spark-1.3-contributor Agent-Effort: unknown Dispatch-Id: dlg-20260923-082148-152a8c Task-Id: MediaResolverAPI-20260923-01
Classify now returns tuples; empty-envelope chain asserts 3 attempts. New: transient-then-hit, exhausted VideoNotFoundError with attempts, tri-state distinctness, cross-boundary desensitization (exc + resolve API), no-retry on ProviderError. Stream media tests stub _call_endpoint. Task-Id: Fixes-Issue: #34, #33 Agent-Executor: ocgo-muse-pi Agent-Model: muse-spark-1.3-contributor Agent-Effort: unknown Dispatch-Id: dlg-20260923-082148-152a8c Task-Id: MediaResolverAPI-20260923-01
…POST Error shape no longer forks on HTTP verb: _call_endpoint raises EndpointHttpError(name, status, body) for every non-2xx dict body so _run_chain always runs classify + has_playable and records http_status (single-endpoint chains like Twitter included). Rewrite the two _call_endpoint contract tests to assert the raise (+ body/status + classify conclusion); add a chain-level case proving the body still reaches the classifier (DouyinTerminalError short-circuit). Task-Id: Fixes-Issue: #34, #33 Agent-Executor: ocgo-muse-pi Agent-Model: muse-spark-1.3-contributor Agent-Effort: unknown Dispatch-Id: dlg-20260923-083824-a438d8 Task-Id: MediaResolverAPI-20260923-01
Agent-Executor: pi-lead Agent-Session: pi-lead-1790151094413-2777230
Required Gate v2 — 状态面板当前状态:fail · 要修代码 当前裁决: Gate 历史(v1;来源为持久化
|
| Run | Attempt | Head | 状态 | 收件人动作 |
|---|---|---|---|---|
| 35839228776 | 1 | a3bdc81 |
fail |
要修代码 |
| 35841611401 | 1 | 187b223 |
fail |
要修代码 |
历史行按 run_id + run_attempt 去重并只增不删;删除本评论后可由 gate_terminal 制品重建。
F1: _should_retry reserves per_timeout + backoff; TimeoutError with retryable attempts below the cap converts to VideoNotFoundError (first-hop hangs and single-endpoint chains unchanged). F2: describe_failure only carries int object_type scalars, non-int yields a fixed type name; _error_body_reason pins int status + str message. Extend desensitization test with dict-object_type chain coverage; add budget-truncation test; append progress paragraph (F3). Task-Id: Fixes-Issue: #34, #33 Agent-Executor: ocgo-muse-pi Agent-Model: muse-spark-1.3-contributor Agent-Effort: unknown Dispatch-Id: dlg-20260923-090551-d1d4f8 Task-Id: MediaResolverAPI-20260923-01
Collaborator
Author
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.
为什么
2026-09-23 12:23 生产一次瞬时的上游空结果把视频号解析打成终态失败(
All providers failed ... attempts=[{'endpoint': 'fetch_video_detail', 'decision': 'retryable'}]),调用方按终态处理、用户任务失败并收告警;同链接 15:59 重放完全正常(issue #34)。事后又答不出「12:23 到底哪一种失败」,因为retryable只记了一个词(issue #33)。根因
retryable的语义是「可能瞬时,值得再来一次」,但引擎对它的唯一补救动作是「换下一个端点」——「重试」和「换源」是两个正交的轴,被合并成了一个动作。多头链上这条错配被兜底掩盖,视频号(TikHub 单源、单端点、无 Cobalt)上直接退化成终态。佐证:下载侧fetch_wechat_channels_media自己手搓了 3 次循环,解析侧一次都没有——同一行为两套实现,行为必然漂移。改法
_run_chain(max_attempts_per_endpoint=..., retry_backoff=...),默认1= 现状(其余 8 平台逐字节不变);视频号链传3/0.3s。只对decision=retryable重试;http_error(含超时)与parse_failed各只算一次尝试。fetch_wechat_channels_media改为单次调用(_fetch_wechat_channels内部即 3 次尝试),其后的 media 缺失/不完整/file_size非法等终态转换原样保留。不下移会叠加成 3×3=9 次上游请求。attempt,既有endpoint/decision键名与取值不变),retryable时带脱敏原因:data_missing—— 响应非 dict / 缺data/data非 dict 或空;object_type_mismatch——data是 dict 但object_type != 0(只带 int 标量;非 int 只带类型名,容器绝不进日志);error_body—— 非 2xx 且 body 是 JSON(附 inthttp_status与上游message截断片段)。classify契约向后兼容扩展为str(等价(decision, None))或(decision, reason),由引擎归一化;_call_endpoint非 2xx 且 body 为 dict 时一律抛EndpointHttpError(name, status, body)(与 HTTP verb 无关),链上仍以该 body 跑 classify + has_playable,保持既有终态短路与命中语义。VideoNotFoundError」是硬保证(gate primary R1 的 major,已收口):per_timeout + retry_backoff(为下一次尝试的完整单端超时留余量),不足即不再重试、按全链未命中收场;max_attempts_per_endpoint > 1)且已记录retryable且尝试次数未达上限 → 改抛VideoNotFoundError(带 attempts)。首跳就挂起、单端点链(上限 1)的既有行为一律不变,两处既有test_chain_total_budget_timeout(微信/抖音)原样保持绿。实测
uv run --extra dev python -m pytest tests/ -q→ 421 passed_call_endpoint内部契约变更只改写抖音/小红书各一条对应用例(断言异常 +.body/.status,保留「body 交分类器」的 codex #4 本意)len({data_missing, object_type_mismatch, error_body}) == 3full_url/decode_key/url_token)后:str(exc)与POST /api/resolve响应文本中三值零出现;同时断言上游message片段确有出现(防恒真)。另覆盖data.object_type为容器(内含凭据)那一形态:不再进 attempts,只留类型名ProviderError: chain timed out(attempts 仅 1 条),修复后VideoNotFoundError(attempts 带 retryable)。新增确定性测试test_chain_budget_truncation_still_not_found(sleep 0.4 / 预算 0.7 / per_timeout 0.05 / backoff 0)锁住超时兜底分支_call_endpoint调用数 == 3、异常类型 isVideoNotFoundError(非 timed out)ProviderError路径调用数 == 11 == 3面值红;R1:raise EndpointHttpError注入return body→ 两条契约用例DID NOT RAISE红;R2:超时兜底条件> 1注入> 99→ 新用例以异常类型面值转红,isinstance(ot,int)注入isinstance(ot,object)→ 脱敏用例以'object_type' not in {...}面值红。三次均单行还原,主脑复跑确认影响范围
只影响视频号链(重试 + attempts 字段)与
_call_endpoint的内部异常形状(链上行为等价,见上表回归证据)。不新增配置项、不新增端点、不改响应结构与缓存。备注
a3bdc81是主脑的 CHANGELOG 记账(运维备忘:生产 PUBLIC_BASE_URL 已设为内网直连地址(2026-09-02) #15 的启动告警条目,PR fix(startup): PUBLIC_BASE_URL 留空时启动打 WARNING 提示 (#15) #35 合并时声明由持有CHANGELOG.md写权的本卡补齐),非实现改动。message),判定为等价实现并接受。Refs #34、Refs #33(关单由主脑在合入后带证据执行)。