feat: preserve governed OpenAI tool messages in 1.4.0

Add faithful single-choice chat results, tool stream activity, error-frame retries, and isolated protocol caching.

Reject empty cached completions with the same value predicate as transport, add regression coverage, and adopt Apache-2.0.
This commit is contained in:
2026-10-01 14:32:19 -04:00
parent 0e29deac1a
commit 9185eab8c9
42 changed files with 2227 additions and 78 deletions
+4 -9
View File
@@ -78,6 +78,7 @@ class StallHedgeTransport:
reasoning_effort,
first_token_event,
content_event,
response_mode,
):
self.calls.append(source.name)
self.ft_events.append(first_token_event)
@@ -271,9 +272,7 @@ class TestStallHedgeTrigger:
"""参数化四格(设计 §5.2 逐字): 两阈值独立生效,各格触发语义不串。"""
# 格 1: 仅 AFTER_S + 流式 pre-TTFT 挂起 → AFTER_S 触发(1.3.7 语义不变)
s1, s2 = _src("s1"), _src("s2")
transport = StallHedgeTransport(
{"s1": [("hang",), ("ok", "late")], "s2": [("ok", "fast")]}
)
transport = StallHedgeTransport({"s1": [("hang",), ("ok", "late")], "s2": [("ok", "fast")]})
mw, _, _ = _harness([s1, s2], transport, hedge_after_s=_STALL_AFTER_S)
ctx = _ctx()
resp = await asyncio.wait_for(mw(_req(ctx=ctx)), timeout=5)
@@ -312,9 +311,7 @@ class TestStallHedgeTrigger:
# 格 3b: 两者皆设 + pre-TTFT 挂起 → 阶段 1 AFTER_S 触发(两阶段各管一段)
s1, s2 = _src("s1"), _src("s2")
transport = StallHedgeTransport(
{"s1": [("hang",), ("ok", "late")], "s2": [("ok", "fast")]}
)
transport = StallHedgeTransport({"s1": [("hang",), ("ok", "late")], "s2": [("ok", "fast")]})
mw, _, _ = _harness(
[s1, s2], transport, hedge_after_s=_STALL_AFTER_S, stall_after_s=_STALL_AFTER_S
)
@@ -340,9 +337,7 @@ class TestStallHedgeTrigger:
async def test_stall_only_non_stream_degrades_to_single_leg(self):
"""仅 stall 已设 + `stream=False` → 逐字单路(中段沉默无非流式概念,入口门)。"""
s1, s2 = _src("s1"), _src("s2")
transport = StallHedgeTransport(
{"s1": [("hang",), ("ok", "x")], "s2": [("ok", "other")]}
)
transport = StallHedgeTransport({"s1": [("hang",), ("ok", "x")], "s2": [("ok", "other")]})
mw, _, _ = _harness([s1, s2], transport, stall_after_s=_STALL_AFTER_S)
ctx = _ctx()
task = asyncio.ensure_future(mw(_req(stream=False, ctx=ctx)))