feat: add a required first-token event to the transport port

Transport.complete gains the keyword-only first_token_event (no default,
per the port convention): streaming sets it on the first delta, the
non-streaming path accepts it but never sets it, None means the caller
does not observe the first token. All fake/wrapping transports and the
three direct call sites follow the signature; the e2e wrapper forwards.

Red-green evidence: tests/outputs/137/t1/ (batch A TypeError red, then
147 file tests + 1550 unit tests green).
This commit is contained in:
2026-09-10 13:16:38 -04:00
parent 166b2865d0
commit 0a6d6225db
12 changed files with 114 additions and 9 deletions
+6
View File
@@ -5,6 +5,7 @@
时间量纲一律**秒**(CHS Redis 实现内部的毫秒换算是后端私事,不进契约)。
"""
import asyncio
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from enum import StrEnum
@@ -46,6 +47,10 @@ class Transport(Protocol):
该参数**不设默认值**,与 `TelemetryRecorder.record_llm_call` 同一既有约定:
库外无第三方实现者,写全签名的成本为零,而默认值会把"某一层漏传"变成静默的
"调用方没表态"——一次本该报错的漏配就此变成一次悄悄涨价的调用。
`first_token_event` 同一约定(1.3.7 对冲 H2): `None` = 调用方不观测首 token
(未启用对冲);非流式实现**永不置位**(物理上无中途信号,事件自然退化为纯时间
阈值),流式实现在首个增量(内容或思考)到达时置位。
"""
async def complete(
@@ -57,6 +62,7 @@ class Transport(Protocol):
overlay: dict[str, Any],
call_id: str,
reasoning_effort: Effort | None,
first_token_event: asyncio.Event | None,
) -> TransportResult: ...