refactor: share one admission path across the three governance loops

_pick_runnable and _on_no_runnable lived in three copies (retry.py,
embedding.py, ocr.py), the latter two being verbatim subsets of the
first. Admission semantics keep evolving -- issue #8 changed the stall
accounting, M2.5 added the AIMD pacer, issue #14 is about to add a wait
policy -- and every round had to be applied three times.

SourceAdmission now owns picking a runnable source and deciding what
happens when none is available. The three loops keep their QuotaGate,
BreakerGate and pacer references because _attempt still needs them for
write-back and pacer.leave(); those instances are shared, not rebuilt
(a second pacer would split the in-flight counter). The cooldown memo
moves in wholesale since only admission consumes it.

Behaviour is unchanged: pick differs from the old chat copy only by the
pacer None-guards, on_no_runnable is verbatim identical, and the suite
reports the same 967 passed / 21 skipped / 32 deselected as before. The
one visible change is the settle-and-release warning text, which had
three variants ("permit", "embedding permit", "OCR permit") and is now
one. Tests importing _demote_call_failures follow it to its new home.
This commit is contained in:
2026-08-19 23:57:01 -04:00
parent 0b3e84b3be
commit 942af99856
5 changed files with 306 additions and 322 deletions
+2 -2
View File
@@ -629,7 +629,7 @@ class TestDemotionInsertPosition:
async def test_demoted_lands_before_junk_sources(self):
# a 失败 2 次;b 可信(0.9)但会被跳过时,第三候选应是 a 而非垃圾源 c
from polygateway.middleware.retry import _demote_call_failures
from polygateway.middleware.admission import _demote_call_failures
srcs = [_src("a"), _src("b"), _src("c")]
health = {"a": 0.9, "b": 0.9, "c": 0.05}.__getitem__
@@ -637,7 +637,7 @@ class TestDemotionInsertPosition:
assert [s.name for s in out] == ["b", "a", "c"]
async def test_health_blind_demotion_still_tail(self):
from polygateway.middleware.retry import _demote_call_failures
from polygateway.middleware.admission import _demote_call_failures
srcs = [_src("a"), _src("b"), _src("c")]
out = _demote_call_failures(srcs, {"a": 2}, None)