fix: address independent verification findings

Classify empty completions as transient per human ruling (fixes flaky
real-gateway smoke and prevents caching empty responses), rename the
factory injection parameter gate to breaker per the frozen design,
rewrite the probe-entry cleanup without except BaseException, declare
python-dotenv explicitly, add a mid-backoff cancellation test, and
record all implementation errata in the design and architecture docs.
This commit is contained in:
2026-07-20 22:01:26 -04:00
parent 0f4ae5ee8b
commit 0b8460b6d5
12 changed files with 101 additions and 16 deletions
+33
View File
@@ -298,6 +298,39 @@ class TestCancellation:
await task
assert (await limiter.source_stats("a")).inflight == 0 # finally 释放
async def test_cancel_mid_backoff_propagates_with_no_held_permit(self):
"""退避 sleep 中取消: CancelledError 穿透,且 permit 早已在 finally 释放。"""
clock = FakeClock()
src = _src("a", max_concurrency=1)
limiter = InMemoryLimiter(
scope="llm",
sources={"a": src},
global_limits=_NO_GLOBAL,
lease_ttl_s=100.0,
now=clock,
)
mw = RetryMW(
scope="llm",
sources=[src],
selector=RoundRobinSelector(),
limiter=limiter,
gate=InMemoryGate(config=_BREAKER, now=clock),
transport=FakeTransport([TransientError("x"), _ok()]),
retry=RetryPolicy(max_attempts=3, backoff_base_s=30.0, backoff_max_s=60.0),
backpressure=BackpressurePolicy(300.0, 0.01),
cooldown_memo=SourceCooldownMemo(now=clock),
emitter=None,
now=clock,
sleep=asyncio.sleep,
rng=lambda: 0.5,
)
task = asyncio.ensure_future(mw(_REQ))
await asyncio.sleep(0.05) # 第一次失败后进入 30s 真实退避
task.cancel()
with pytest.raises(asyncio.CancelledError):
await task
assert (await limiter.source_stats("a")).inflight == 0 # 退避期不占并发槽
async def test_cancel_probe_releases_probe_lease(self):
clock = FakeClock()
mw, _, gate, _, _, _ = _harness(