test: pin fence/TTFP semantics for substantive-progress gating (#96667 salvage)

Follow-up for the salvaged substantive-progress fix, adapted to main's
three-hook architecture:

- Keep the dispatch tick and the completed-response shim tick (both
  deliberate on main; one-shot terminal events that cannot defeat an
  inactivity timeout) — update the two PR assertions accordingly.
- Pin the end-to-end bug: keepalive/empty-role chunks must leave
  CompressionCommitFence stale, a substantive token must refresh it.
- Pin the fast-lane telemetry contract (#96945/#96963):
  time_to_first_progress_ms records on the first frame of any kind via
  the split-out _notify_aux_timing_response helper.
This commit is contained in:
kshitijk4poor
2026-08-29 10:40:11 +05:30
committed by kshitij
parent 7ff70f1709
commit 436435bfc9

View File

@@ -143,7 +143,9 @@ class TestCreateWithProgress:
assert result.choices[0].message.reasoning == "thinking..."
assert result.choices[0].finish_reason == "stop"
assert result.usage.total_tokens == 7
assert ticks == [1, 1, 1]
# 1 dispatch tick (preserved for the watchdog's historical liveness
# signal — see _create_with_progress) + 1 per substantive chunk.
assert ticks == [1, 1, 1, 1]
def test_completed_response_without_stream_payload_does_not_tick(self):
calls = []
@@ -162,7 +164,11 @@ class TestCreateWithProgress:
assert calls[0]["stream"] is True
assert result is _COMPLETE
assert ticks == []
# A completed response object carries the full summary payload, and
# the dispatch tick is the watchdog's historical liveness signal:
# both are one-shot terminal ticks, not per-frame keepalives, so
# neither can defeat an inactivity timeout.
assert ticks == [1, 1]
def test_streaming_rejected_falls_back_to_plain_call(self):
client = _FakeClient(
@@ -423,6 +429,56 @@ class TestContentBearingProgress:
assert touches == [1, 1]
def test_keepalive_chunks_do_not_reset_the_compression_fence(self):
"""End-to-end bug pin (#96707): content-free frames must not refresh
CompressionCommitFence._last_progress.
The waiter in conversation_compression charges its idle budget from
seconds_since_progress(); before the fix, every keepalive chunk fed
through _ChatStreamAccumulator ticked the fence, so a stalled
summary stream never hit the inactivity timeout."""
fence = CompressionCommitFence()
accumulator = _ChatStreamAccumulator()
keepalive = SimpleNamespace(id=None, model=None, choices=[], usage=None)
empty_role_chunk = _chunk(content="", reasoning="")
with aux_progress_hook(fence.touch_progress):
for _ in range(5):
accumulator.feed(keepalive)
accumulator.feed(empty_role_chunk)
# No substantive payload arrived: the fence must have stayed stale.
assert fence.seconds_since_progress() > 0.0
with aux_progress_hook(fence.touch_progress):
accumulator.feed(_chunk(content="token"))
assert fence.seconds_since_progress() < 0.05
def test_content_free_frames_still_record_ttfp_timing(self):
"""The fast-lane telemetry contract (#96945/#96963) survives the
gating: time_to_first_progress_ms must record on the FIRST frame of
any kind (transport liveness), not only on the first token."""
from agent.auxiliary_client import (
_aux_provider_response,
_aux_timing_hook,
_notify_aux_timing_response,
)
timings: dict = {}
def _timed_response() -> None:
timings.setdefault("time_to_first_progress_ms", 42)
keepalive = SimpleNamespace(id=None, model=None, choices=[], usage=None)
accumulator = _ChatStreamAccumulator()
with (
_aux_timing_hook(_aux_provider_response, _timed_response),
aux_progress_hook(lambda: None),
):
accumulator.feed(keepalive)
assert timings["time_to_first_progress_ms"] == 42
# ---------------------------------------------------------------------------
# Ceiling arithmetic