docs: fix plan per independent review (B1 emitter fake sync, B2 guard return contract, N1-N5)
This commit is contained in:
@@ -44,7 +44,7 @@ Skill 纪律:T1–T4 行为变更执行 `test-driven-development`(先红后绿
|
||||
| 修改 | `src/polygateway/ports.py` | `Transport.complete`(:56-66)加 `content_event` 必填 kw + docstring;`TelemetryRecorder.record_llm_call`(:306-344)加 `stall_ms`/`stall_count` 两参 + docstring("36 字段"→"38 字段");`TransportResult` 在 types.py(见下) |
|
||||
| 修改 | `src/polygateway/types.py` | `TransportResult`(:560-586)增 `stall_ms: float \| None = None`/`stall_count: float \| None = None`(追加在 `applied_effort` 后);`CallStats`(:296-324)在 `hedge_won` 后增 `stall_ms: int \| None = None`/`stall_count: int \| None = None`;`_CallContext`(:328-388)`__slots__`/`__init__` 增两私有位、`record_generation` 增 keyword-only `stall_ms`/`stall_count`(默认 None)、`snapshot` 填两字段;`SourceConfig`(:590-700)在 `effort_fallback` 后追加 `stall_threshold_ms: int = 1000` + `__post_init__` 值域校验(正整数、拒 bool) |
|
||||
| 修改 | `src/polygateway/transports/openai_compat.py` | `complete`(:427-473)加 `content_event` 透传两路径;`_complete_stream`(:553-616)chunk 循环(:580-590 邻域)记增量 chunk 时间戳 + 每 chunk `set` content_event(None 跳过),装配处(:597-616 邻域)由序列算 stall 两值;模块级纯函数 `_stall_from_chunk_times`;`_complete_once`(:660-705)接收 content_event 但**永不置位**(docstring 明写),stall 两值恒 None(默认) |
|
||||
| 修改 | `src/polygateway/middleware/retry.py` | `__call__` 入口门(:273-283)改两阈值判定;`_attempt`(:304-400)加 `content_event` 私有 kw、sink 元素改三元组、成功分支取整换算 + `_emit` 传 stall;`_attempt_hedged`(:371-530)Phase 1/4 接线 content_event、Phase 5 汇合复用;`_past_hedge_window`(:533-562)两阶段重构;`_record_generation`/`_register_hedge` 邻域(净口径计算收敛一处);`__init__` 加 `stall_after_s` kw |
|
||||
| 修改 | `src/polygateway/middleware/retry.py` | `__call__` 入口门(:273-283)改两阈值判定;`_attempt`(:304-400)加 `content_event` 私有 kw、sink 元素改三元组、成功分支取整换算 + `_emit` 传 stall;`_attempt_hedged`(:415-531)Phase 1/4 接线 content_event、Phase 5 汇合复用;`_past_hedge_window`(:533-562)两阶段重构;`_record_generation`/`_register_hedge` 邻域(净口径计算收敛一处);`__init__` 加 `stall_after_s` kw |
|
||||
| 修改 | `src/polygateway/config.py` | `_RESERVED_SEGMENTS`(:57)加 `"STREAM"`;`GatewaySettings`(:192-196 后)加 `stall_threshold_ms: int = 1000`、`hedge_stall_after_s: float \| None = None`;`_validate_hedge`(:382-392)扩传新参;`from_env`(:398-427)`**_load_stream(...)`;新增 `_load_stream`(:745 `_load_hedge` 邻域);`check_hedge_assembly`(:772-849)加 `stall_after_s` 参与三守卫;`EmbeddingSettings.__post_init__`(:865-869 邻域)与 `OcrSettings`(新增 `__post_init__`)加 B4 对冲键 warning |
|
||||
| 修改 | `src/polygateway/client.py` | `__init__`(:240 邻域)加 `hedge_stall_after_s`/`stall_threshold_ms` keyword-only 参数,守卫(:252-257)扩传;`stall_threshold_ms` 经 `dataclasses.replace` 分发进各 SourceConfig;RetryMW 构造(:264-283)传 `stall_after_s`;`from_settings`(:485-526)透传 |
|
||||
| 修改 | `src/polygateway/middleware/telemetry.py` | `_AttemptUsage`(:199-236)增两字段;`emit_attempt`(:396-447)加 `stall_ms`/`stall_count` kw(默认 None)+ 转发;`emit_cache_hit`/`emit_terminal_failure`(:449-540)显式传 None;`_record`(:560-665)加两参并在 :618 调用点透传 |
|
||||
@@ -106,7 +106,8 @@ Skill 纪律:T1–T4 行为变更执行 `test-driven-development`(先红后绿
|
||||
| 5 | `tests/unit/test_ports.py:102`(`_DummyRecorder`,**显式全签名**) | +2 参(它镜像协议全签名,不加则与本协议脱节) |
|
||||
| 6 | `tests/unit/test_telemetry.py:3327/:3349/:3354`(装配闸**负例** fake,故意残缺签名) | **不改**——协议加参后它们仍不满足签名,负例断言照常通过;T3 绿态核对这三条仍绿 |
|
||||
| 7 | `**fields` fake(13 处):`test_ocr_client.py:149`、`test_pricing.py:131`、`test_openai_compat.py:106`、`test_embedding.py:246`、`test_cache.py:562`、`test_client.py:528/955/968/1516/1842`、`test_usage_source_domain.py:71`、`test_telemetry.py:1191/2059` | **零改动**自动兼容;批次 C 断言落行时用其中一份捕获 fields |
|
||||
| 8 | 发射器 fake:`tests/unit/test_hedge.py:122`、`tests/unit/test_backpressure.py:233`(`emit_attempt`) | **零改动**(`emit_attempt` 新参默认 None,见 §3.4 取舍) |
|
||||
| 8a | 发射器 fake:`tests/unit/test_backpressure.py:233`(`*args, **kwargs` 形态) | **零改动**(`emit_attempt` 新参默认 None,见 §3.4 取舍) |
|
||||
| 8b | 发射器 fake:`tests/unit/test_hedge.py:122`(RecordingEmitter,显式全签名 8 kw,无 `**kwargs`) | **列入 T3 同步清单**(审查 B1):加 `stall_ms=None, stall_count=None` 两 kw——T3 后 `_emit` 显式转发两 kw,不加则 TypeError;"新参默认 None"只护调用方、护不住被调的显式签名 fake |
|
||||
|
||||
## 3. 跨任务接口(可执行定义,禁止占位)
|
||||
|
||||
@@ -227,8 +228,8 @@ generation_sink.append((wall_ms, stall_ms_i, stall_count_i))
|
||||
| --- | --- |
|
||||
| loader | `_load_hedge`(:745-769)扩读 `STALL_AFTER_S`:`_first` + `_cast` + `ensure_call_deadline`(origin 传实际命中键名),返回字典加 `"hedge_stall_after_s"` 键;未设 = None(关闭) |
|
||||
| 字段 | `GatewaySettings` 加 `hedge_stall_after_s: float \| None = None`;`_validate_hedge`(:382-392)扩传 |
|
||||
| 守卫(全部落进 `check_hedge_assembly` :772-849,签名加 `stall_after_s` 参,`None` 时值域归一化后交叉守卫不查——同 `after is None` 既有早退形态) | ①值域 = `ensure_call_deadline` 同款(有限正数);②`stall_after_s ≥ min over sources of (inter_token_timeout_s or timeout_s)`(有效最小值,与 openai_compat.py:573 回落同款)→ **ValueError**(对冲窗永不开,装配期直接拒绝;stall 场景无非流式残留收益,warning 论证不足,N5);③`stall_after_s ≥ call_deadline_s`(两者皆设)→ ValueError(与 `after_s` 同款,config.py:825-828 邻域);④单源 scope 设阈值 → warning 允许(1.3.7 §5 同款);⑤**B4**:Embedding/OCR scope 装配 warning 落 `EmbeddingSettings.__post_init__`(:865-869 邻域)与 `OcrSettings`(新增 `__post_init__`):内嵌 gateway 的 `hedge_after_s` **或** `hedge_stall_after_s` 非 None → warning"对冲键仅对 chat scope 生效,本 scope 非流式静默退化"(1.3.7 `AFTER_S` 同病顺带补齐,缺陷修复不扩公共面);chat 侧装配 warning 明确不做(per-call stream,装配期无信号,B4 裁决) |
|
||||
| client | `GatewayClient.__init__` 加 `hedge_stall_after_s: float \| None = None` keyword-only,:252-257 守卫调用扩传;RetryMW 构造(:264-283)传 `stall_after_s=` 归一化值;`from_settings`(:525 邻域)透传 |
|
||||
| 守卫(全部落进 `check_hedge_assembly` :772-849,签名加 keyword-only `stall_after_s` 参;**返回契约钉死(审查 B2)**:返回 `tuple[float | None, float | None]` = (归一化 after_s, 归一化 stall_after_s),`config.py:384` 与 `client.py:252` 两处调用点随之解构,`test_config.py:1174/:1184` 列入 T4 同步清单(`is None` 断言按新返回形态改写);**两阈值皆 None 才早退**——"同 `after is None` 早退形态"字面执行会让仅 stall 配置绕过守卫②③④,禁止) | ①值域 = `ensure_call_deadline` 同款(有限正数);②`stall_after_s ≥ min over sources of (inter_token_timeout_s or timeout_s)`(有效最小值,与 openai_compat.py:573 回落同款)→ **ValueError**(对冲窗永不开,装配期直接拒绝;stall 场景无非流式残留收益,warning 论证不足,N5);③`stall_after_s ≥ call_deadline_s`(两者皆设)→ ValueError(与 `after_s` 同款,config.py:825-828 邻域);④单源 scope 设阈值 → warning 允许(1.3.7 §5 同款);⑤**B4**:Embedding/OCR scope 装配 warning 落 `EmbeddingSettings.__post_init__`(:865-869 邻域)与 `OcrSettings`(新增 `__post_init__`):内嵌 gateway 的 `hedge_after_s` **或** `hedge_stall_after_s` 非 None → warning"对冲键仅对 chat scope 生效,本 scope 非流式静默退化"(1.3.7 `AFTER_S` 同病顺带补齐,缺陷修复不扩公共面);chat 侧装配 warning 明确不做(per-call stream,装配期无信号,B4 裁决) |
|
||||
| client | `GatewayClient.__init__` 加 `hedge_stall_after_s: float \| None = None` keyword-only,:252-257 守卫调用扩传;RetryMW 构造(:264-283)传 `stall_after_s=` 归一化值(取守卫返回二元组第二元,B2);`from_settings`(:525 邻域)透传 |
|
||||
|
||||
### 3.6 T4:retry 编排改动(P2 唯一实现形态)
|
||||
|
||||
@@ -340,12 +341,13 @@ async def _past_hedge_window(self, primary, first_token, content_event) -> bool:
|
||||
### T3 → 提交 3 `feat: add stall_ms and stall_count telemetry columns`
|
||||
|
||||
- **目标**:遥测两列全链路(schema 五编辑点 + 协议两参 + emitter 透传 + 两 recorder 自动拾取);§2.3 同步清单落地。
|
||||
- **改动**:`telemetry/schema.py` 五编辑点(§3.4 表)+ 模块头计数 docstring;`ports.py:306-344`;`middleware/telemetry.py:199-236/396-447/449-540/560-665`;`retry.py:_emit`(§3.4);`sqlite.py:145`/`postgres.py:411` docstring 计数;`test_ports.py:102` +2 参;`test_telemetry.py` 冻结清单扩展;`tests/integration/test_postgres_telemetry.py:33-77` 冻结清单扩展。
|
||||
- **改动**:`telemetry/schema.py` 五编辑点(§3.4 表)+ 模块头计数 docstring;`ports.py:306-344`;`middleware/telemetry.py:199-236/396-447/449-540/560-665` + `:368` docstring 计数("组装 36 字段"→38,审查 N4);`retry.py:_emit`(§3.4);`sqlite.py:145`/`postgres.py:411` docstring 计数;`test_ports.py:102` +2 参;`test_telemetry.py` 冻结清单扩展;`tests/integration/test_postgres_telemetry.py:33-77` 冻结清单扩展。
|
||||
- **先失败测试清单**(批次 C;先红 = 冻结清单已扩展而 schema 未改 → 列数/列序断言失败;协议未加参 → 装配闸 ValueError):
|
||||
|
||||
| 测试名 | 断言点 |
|
||||
| --- | --- |
|
||||
| `test_telemetry.py::TestSchemaFreeze::test_columns_and_ddl_are_frozen`(扩) | `_EXPECTED_COLUMNS` 末尾追加两列后:`len(COLUMNS) == 38`;两端 DDL 首现顺序含新列;`PG_BACKFILL[-1]` 变 stall_count 项;占位符 `$38)`/`?`×38 |
|
||||
| `test_ports.py:313` 与 `test_telemetry.py:748` 硬编码计数(扩,审查 N1) | `len(params)-1 == 36` → 38;`len(physical) == 37` → 39;连带 :650/:743/:756 注释文本同步——两处列入批次 C 清单,防被实施者"发现"而非计划登记 |
|
||||
| `test_telemetry.py::TestBackendColumnParity::test_new_columns_are_appended_last`(扩) | `COLUMNS[-2:] == ("stall_ms", "stall_count")` |
|
||||
| `test_telemetry.py::<SQLite 落行新用例>` | emit attempt(stall_ms=3200, stall_count=2)→ SQLite 行两列恰为该值;success 非流式行两列 NULL |
|
||||
| `test_telemetry.py::<迁移幂等新用例>` | 既有 1.3.5 形态表(37 物理列)开 auto_migrate recorder → ALTER 补两列不炸;**重复初始化第二次**幂等;manual 档缺列告警逐字含两条新 ALTER |
|
||||
@@ -359,8 +361,8 @@ async def _past_hedge_window(self, primary, first_token, content_event) -> bool:
|
||||
### T4 → 提交 4 `feat: add opt-in mid-stream stall hedging for chat`
|
||||
|
||||
- **目标**:`HEDGE__STALL_AFTER_S` 独立生效的中段对冲 + `STREAM__STALL_THRESHOLD_MS` 装配三层可调 + 全部守卫(B4 含 1.3.7 AFTER_S 同病补齐)。
|
||||
- **改动**:`config.py:57/192-196/382-392/398-427/745-769/772-849/865-869/OcrSettings 新 __post_init__`(§3.5);`client.py:240/252-257/264-283/485-526`(§3.5);`retry.py:__init__/:273-283/:371-530/:533-562`(§3.6)。
|
||||
- **先失败测试清单**(批次 D;先红 = 键未识 ValueError/None 断言失败、对冲未触发 `hedges==0`、挂起用例超时):
|
||||
- **改动**:`config.py:57/192-196/382-392/398-427/745-769/772-849/865-869/OcrSettings 新 __post_init__`(§3.5);`client.py:240/252-257/264-283/485-526`(§3.5);`retry.py:__init__/:273-283/:415-531/:533-562`(§3.6)。
|
||||
- **先失败测试清单**(批次 D;先红 = 键未识时 3 段键被 `config.py:431` 静默跳过、实际红因 AttributeError/TypeError(审查 N3 措辞修正,红仍成立)、None 断言失败、对冲未触发 `hedges==0`、挂起用例超时):
|
||||
|
||||
| 测试名(新建 `tests/unit/test_stall_hedge.py`,设施复用 test_hedge.py 范式:两源、event-driven 假 transport、真实 loop 钟、阈值 0.05s、容差 4–10×) | 断言点 |
|
||||
| --- | --- |
|
||||
@@ -378,7 +380,7 @@ async def _past_hedge_window(self, primary, first_token, content_event) -> bool:
|
||||
| `test_config.py::test_stall_after_s_guard_deadline_conflict_raises` | `≥ call_deadline_s` → ValueError |
|
||||
| `test_config.py::test_stall_after_s_single_source_warns` | 单源 warning 允许(loguru 捕获 fixture,test_config.py:31-33 既有) |
|
||||
| `test_config.py::test_embedding_ocr_scope_hedge_keys_warn` | Embedding/OCR settings 内嵌 gateway 的 AFTER_S **或** STALL_AFTER_S 非 None → warning(B4,含 1.3.7 同病补齐);chat 侧无此 warning(B4 裁决) |
|
||||
| 批次 E 默认关闭回归门 | 两键均不设 → `pytest tests/unit tests/contracts -q` 全绿且**不改一行既有断言**(1.3.6/1.3.7 取消/对冲/看门狗语义回归) |
|
||||
| 批次 E 默认关闭回归门 | 两键均不设 → `pytest tests/unit tests/contracts -q` 全绿且**不改一行既有语义断言**(例外登记——审查 B1/B2/N1:fake 签名同步、`test_config.py:1174/:1184` 返回形态断言、两处硬编码计数,均属签名/清单同步,非语义断言改动) |
|
||||
|
||||
- **证据出口**:`tests/outputs/138/t4/red-batch-d.log` + `green-unit.log` + `green-contracts.log` + `lint.log`。
|
||||
- **验收命令**:`pytest tests/unit/test_stall_hedge.py tests/unit/test_config.py -q` → `pytest tests/unit tests/contracts -q` → `make lint`。
|
||||
@@ -408,7 +410,7 @@ async def _past_hedge_window(self, primary, first_token, content_event) -> bool:
|
||||
| D | 中段对冲编排(8 用例)+ 配置守卫(6 用例) | T4 | test_stall_hedge.py(新)/ test_config.py |
|
||||
| E | 默认关闭回归门(不改一行既有断言) | T4 门 | tests/unit + tests/contracts 全套件 |
|
||||
|
||||
**设施复用核对(动手前必读)**:`test_retry.py:120-127` FakeSleep 只记录不推进时钟(推进须用例自带闭包);`test_hedge.py:41-69` 事件驱动假 transport(content_event 身份逐次记录,同款扩展);`test_config.py:31-33` loguru WARNING 捕获 fixture(caplog 抓不到 loguru);取消窗口用 `entered` Event 范式(`test_retry.py:79-89`);真实 loop 钟阈值 0.05s、容差 4–10×(`test_streaming.py` 既有范式,不标 slow);`test_telemetry.py:53-99` 冻结清单的扩展属"先红"手段(先改清单 → schema 未改即红)。
|
||||
**设施复用核对(动手前必读)**:`test_retry.py:120-127` FakeSleep 只记录不推进时钟(推进须用例自带闭包);`test_hedge.py:41-69` 事件驱动假 transport(content_event 身份逐次记录,同款扩展);`test_config.py:31-33` loguru WARNING 捕获 fixture(caplog 抓不到 loguru);取消窗口用 `entered` Event 范式(`test_retry.py:79-89`);真实 loop 钟阈值 0.05s、容差 4–10×(`test_streaming.py` 既有范式,不标 slow);**命名区隔**(审查 N5):`test_hedge.py:150` 既有 `stall_window_s`(StallClock 预算)与新 `stall_after_s`(对冲触发阈值)是不同概念,`test_stall_hedge.py` 复用设施时注释明写;`test_telemetry.py:53-99` 冻结清单的扩展属"先红"手段(先改清单 → schema 未改即红)。
|
||||
|
||||
## 6. 风险节与验收门
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
核查完毕,所有证据齐备。撰写审查报告:
|
||||
|
||||
## Review
|
||||
|
||||
**结论:有条件执行** — 计划整体忠实于已批准设计(含 B1-B4/N1-N9),同步清单两条 grep 重跑基本属实,TDD 红绿逻辑与编排伪代码经源码逐行验证成立;但有 **2 处计划文本级硬伤**(按字面执行必在 T3/T4 验收门炸出计划声称"零改动/不改断言"的返工)需先修计划,另 5 处建议项。无设计级返工、无人类门事项。
|
||||
|
||||
### 8 条核查逐条裁决
|
||||
|
||||
**1. 同步清单真实性 — 基本属实,一处虚列。** 重跑两条 grep:§2.2 的 11 处 `def complete` + 11 处调用点全部命中且行号准确(含 test_retry.py:112 第二 fake 与 :123 转发、e2e/conftest.py:294/:306 转发、test_live_evidence.py:280/:1270、test_usage_source_domain.py:142);§2.3 的 13 处 `**fields` fake 计数精确、3 处负例(:3327/:3349/:3354 均为故意残缺显式签名)、2 处 `**fields` recorder(sqlite.py:145/postgres.py:411)、_DummyRecorder(test_ports.py:102)全部属实。**虚列**:§2.3 #8 声称 test_hedge.py:122"零改动"不成立(显式全签名 fake,见 B1);test_backpressure.py:233 是 `*args, **kwargs` 才真零改动。另漏列两处硬编码计数断言(见 N1)。
|
||||
|
||||
**2. 签名与行号 — 抽查 14 处,绝大多数精确。** 精确命中:ports.py:56/:306(36 参实测)、types.py:296/:328/:560/:581/:590/:633/:674、openai_compat.py:427/:553/:560/:659/:666(chunk 循环 :581-592、装配 :598-616)、retry.py:275/:314/:344/:346/:436/:484/:533-562/:574-578/:580-594、config.py:57/:195/:745-769/:772-849/:865/:904、client.py:240/:252/:280/:485、middleware/telemetry.py:198/:396/:449/:501/:556/:618、schema.py 五编辑点。**漂移**:`_attempt_hedged` 计划 :371-530、实际 **:415-531**;另有 5 处 ±1-6 小漂移(入口门 :268-279、from_env :394-424、_emit :668-700、_complete_once :659-705、_validate_hedge :375-392)。具名接缝均无歧义,不阻断(见 N2)。
|
||||
|
||||
**3. TDD 红绿逻辑 — 四批次全部成立。** 批次 A(端口未加参→TypeError、纯函数不存在→ImportError、字段不存在→TypeError)、批次 B(字段缺失→AttributeError、仍得墙钟值→断言失败)、批次 C(冻结清单先扩→列数/列序断言失败,已验证 `_EXPECTED_COLUMNS` 派生机制;协议未加参而 _DummyRecorder 先加→装配闸 ValueError)、批次 D(kw 不存在→TypeError、`hedges==0`)均为真红,无伪 TDD。措辞瑕疵:批次 D "键未识 ValueError" 实为 AttributeError/TypeError(3 段键被 config.py:431 静默跳过),红仍成立(见 N3)。
|
||||
|
||||
**4. 编排语义 — 并发正确性验证通过,与设计 §5.2 一致。** Event 复检防同 pass 误触发 ✓;check→clear 之间同步无 await(单线程循环下无交错)✓;每轮自建 waiter + finally 收口 + `cancelling()` 穿透逐字套用 :544-561 既有范式 ✓;Event 状态持久无丢失唤醒 ✓;残留竞速窗口已诚实标注(设计 N3① 批准形态)。仅 stall 时"阶段 1 无 timeout 等首 token→再进阶段 2"与设计 §5.2/B3 逐字一致——**pre-TTFT 沉默不被 stall 窗误吞正是设计意图**,TTFT 长等待由 ttft 看门狗治理 ✓。入口门三分支自洽(仅 stall+非流式→单路;AFTER_S+非流式→对冲路,事件退化为纯时间阈值,同 1.3.7)✓。**主路被看门狗掐死**:TransientError 在 `_attempt` except 分支转为 `_Failed` 返回(retry.py:393-406),primary task 正常完成 → Phase 5 "先了结的是失败则等另一路"(retry.py:505-509)原样承接,attempt 级记账(breaker/attempt_fails/遥测 error 行)在返回前已完成 ✓——设计 §11-N9 完全可由既有路径表达。
|
||||
|
||||
**5. 换算与钟纪律 — 成立。** N8 唯一换算处落点核实:retry.py:346 是现状唯一 int 换算点;sink 三元组升级的消费方仅 `__call__`:289、`_record_generation`:578、`_register_hedge`:593 三处,全在 retry.py 且计划 §3.2 已枚举,无测试直调这两个私有函数(grep 实证)。N4 钟同源核实:client.py:243 `now=time.monotonic` 默认与 transport 内 `time.monotonic` 同钟 ✓。`record_generation` 加 kw-only 默认 None 对 embedding.py:391/ocr.py:407/test_types.py:648-669 零冲击 ✓。
|
||||
|
||||
**6. 遥测链路 — 取舍安全,一处 fake 例外。** emit_attempt 加默认 None kw 对全部 ~45 处真实 emitter 直调(均关键字形态,grep 全量清点)与 embedding.py:532/ocr.py:611 安全 ✓;三处装配闸负例在协议 +2 参后仍不满足 `signature.bind`,负例仍绿 ✓(逻辑推理成立:残缺签名缺的不止两参)。**例外即 B1**:RetryMW._emit 是 emit_attempt 的调用方且将显式转发两 kw,"新参默认 None"只保护调用方、不保护被调的显式签名 fake。
|
||||
|
||||
**7. 配置守卫 — 落点属实,返回契约有缺口。** check_hedge_assembly 调用点四处(client.py:252、config.py:384、test_config.py:1174/:1184)全关键字形态,加 kw-only 参可行;有效最小值守卫与 openai_compat.py:573 回落同款 ✓;B4 落点核实:EmbeddingSettings.__post_init__ :865 存在且持 `gateway` 字段 ✓,OcrSettings :904 确无 `__post_init__`(计划"新增"表述准确)✓。**缺口**:归一化 stall_after_s 的返回路径未定义(现状返回 `float|None`),二元组返回会炸 test_config.py:1184 的 `is None` 断言,与批次 E 冲突(见 B2);早退须改写为"两阈值皆 None 才早退"(见 B2 附带)。
|
||||
|
||||
**8. 默认关闭回归 — 论证成立。** T1 恒传 None(retry.py:275 等)✓、入口门第一分支逐字保留 ✓;stall 序列无条件记录(content_event=None 只跳过 set,不跳过时间戳)→ 流式 ≥2 chunk 恒填 0.0/0.0 或实际值、非流式/<2 恒 None——**计量独立于对冲开关正是 P1 意图,设计与计划一致** ✓。TransportResult 新字段不破坏回归门:test_openai_compat.py 无相等断言、无 asdict/fields 计数(grep 实证)✓;CallStats 新字段默认 None 且无 `== CallStats` 断言 ✓。**字面例外**:B1/N1 所列硬编码计数断言与 test_config.py:1184 是批次 E"不改一行既有断言"的真实例外,计划应显式登记而非被实施者发现。
|
||||
|
||||
### 发现列表
|
||||
|
||||
| # | 级别 | 位置 | 问题 | 修法 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| B1 | 阻断(事实错误,修法小) | plan §2.3 #8;tests/unit/test_hedge.py:121-133 | RecordingEmitter.emit_attempt 是显式全签名 fake(收 8 个 kw,无 `**kwargs`);T3 后 `_emit` 显式转发 `stall_ms=`/`stall_count=` → TypeError,test_hedge.py 全部成功路径用例红,且违反批次 D/E "1.3.7 既有断言一字不改"承诺。"新参默认 None"的论证只护调用方,护不住被调 fake | §2.3 #8 拆两行:test_backpressure.py:233(`*args, **kwargs`)真零改动;test_hedge.py:122 列入 T3 同步清单(加 `stall_ms=None, stall_count=None` 两 kw,与 T1 fake 签名同步同款处置);批次 E 措辞澄清"fake 签名同步不属于断言改动" |
|
||||
| B2 | 阻断(契约占位,违反计划自设标准) | plan §3.5 守卫行;config.py:772-849;test_config.py:1171-1190 | 计划要求 "RetryMW 传 stall_after_s=归一化值",但归一化值如何出 check_hedge_assembly 未定义(§3 自称"可执行定义,禁止占位");最自然的二元组返回炸 test_config.py:1184 的 `is None` 断言,与批次 E 直接冲突。附带:§3.5"同 after is None 既有早退形态"字面执行会让仅 stall 配置绕过守卫②③④(批次 D 能抓住,但属误导) | §3.5 钉死返回契约(建议 `tuple[float\|None, float\|None]` 或等值命名结果,config.py:384 与 client.py:252 两处随之解构);test_config.py:1174/:1184 列入 T4 同步清单;早退条件改写为"两阈值皆 None 才早退";批次 E 例外登记本处 + B1 + N1 |
|
||||
| N1 | 建议 | test_ports.py:313、test_telemetry.py:748(连带 :650/:743/:756 注释文本) | 两处硬编码计数断言(`len(params)-1 == 36`、`len(physical) == 37`)未列入批次 C 清单;T3 验收门会自愈,但破坏"清单即全量"承诺 | 补入批次 C 扩展点(36→38、37→39),注释文本顺手同步 |
|
||||
| N2 | 建议 | plan §2.1/§8 行号表 | `_attempt_hedged` :371-530 实为 :415-531;另有 5 处 ±1-6 漂移 | 执行前以 grep 复核为准(§6 已有此纪律),修正 §2.1 表防误锚 |
|
||||
| N3 | 建议 | plan T4 先红清单 | "键未识 ValueError" 不准确:3 段未识键被 config.py:431 静默跳过,实际红因是 AttributeError/TypeError | 措辞修正即可,红成立 |
|
||||
| N4 | 建议 | middleware/telemetry.py:368 | docstring "组装 36 字段" 未列入计数同步范围(ports.py:280/sqlite.py:146/schema.py:8 已覆盖) | T3 顺手同步,防计数漂移 |
|
||||
| N5 | 建议 | tests/unit/test_hedge.py:150 `_harness(stall_window_s=300.0)` | 既有 `stall_window_s`(StallClock 预算)与新 `stall_after_s` 命名相邻,新文件复制设施时易混 | test_stall_hedge.py 设施复用时加注释区隔两概念 |
|
||||
|
||||
**Merge verdict: OK with notes** — B1/B2 修计划文本(均为小时级,不改任何设计语义)后即可按书执行;TDD 门对全部 N 项有自愈能力。
|
||||
Reference in New Issue
Block a user