feat: record call observability columns and terminal failure rows
Grow the telemetry contract from 26 to 36 fields and give every logical call a failure terminal row, so SQL can finally answer "how many calls failed" and "why did the whole pool die". Schema and port move together with the emitter writes in one commit: splitting them would ship columns that nothing populates. - schema: append 10 nullable columns (scope, operation, logical_call_id, event_kind, http_status_code, error_type, cause_type, error_body, attempts, total_latency_ms) to all five definition sites in one order - ports: 10 keyword-only parameters without defaults; the protocol signature is now the single source the assembly gate derives from - emitter: take domain exception objects instead of pre-flattened text and pin down the diagnostics in one helper; a relabelled 503 stays 503 and success rows leave all five columns NULL - emitter: reject recorders whose record_llm_call cannot accept the current field shape at assembly time, since _record would otherwise swallow the TypeError and drop every row while calls keep succeeding - clients: write at most one terminal row per logical call through a single shared exit, deduplicated by the call context; TelemetryMW stops writing terminals so the two sites cannot double count - clients: cancellation stays best effort and propagates, non-domain exceptions get no terminal row and keep their classification - transports: give _status_to_error an explicit operation and fix the historically mislabelled embedding HTTP failures - structured: promote the bounded error formatter so the reask feedback and the terminal explanation share one set of limits Terminal rows carry no cost and no tokens, so cost aggregation is unchanged; failure counts must now filter on event_kind.
This commit is contained in:
@@ -20,11 +20,12 @@ from polygateway.backends.memory.breaker import InMemoryGate
|
||||
from polygateway.backends.memory.cache import InMemoryCache
|
||||
from polygateway.backends.memory.limiter import InMemoryLimiter
|
||||
from polygateway.config import GatewaySettings
|
||||
from polygateway.errors import PolyGatewayError
|
||||
from polygateway.middleware.base import compose
|
||||
from polygateway.middleware.cache import CacheMW
|
||||
from polygateway.middleware.retry import RetryMW
|
||||
from polygateway.middleware.structured import StructuredMW
|
||||
from polygateway.middleware.telemetry import TelemetryEmitter, TelemetryMW
|
||||
from polygateway.middleware.telemetry import TelemetryEmitter, TelemetryMW, emit_terminal_once
|
||||
from polygateway.ports import TelemetryStatusProvider
|
||||
from polygateway.pricing import PricingTable
|
||||
from polygateway.providers import get_provider
|
||||
@@ -239,7 +240,7 @@ class GatewayClient:
|
||||
rng: Any = random.random,
|
||||
) -> None:
|
||||
emitter = (
|
||||
TelemetryEmitter(telemetry, pricing=pricing, text_cap=text_cap)
|
||||
TelemetryEmitter(telemetry, scope=scope, pricing=pricing, text_cap=text_cap)
|
||||
if telemetry is not None
|
||||
else None
|
||||
)
|
||||
@@ -393,7 +394,27 @@ class GatewayClient:
|
||||
meta=dimensions,
|
||||
call_context=context,
|
||||
)
|
||||
response = await self._handler(request)
|
||||
try:
|
||||
response = await self._handler(request)
|
||||
except PolyGatewayError as exc:
|
||||
# 统计边界内的一切领域失败均尝试写一条终态行(1.3.5 设计 §6 I3),
|
||||
# 包括已有 attempt 错误行的 RequestRejected / ResultInvalid——两类行描述
|
||||
# 的不是同一件事(尝试 vs 逻辑终态),由 `event_kind` 区分
|
||||
await emit_terminal_once(
|
||||
self._emitter, request=request, context=context, error=exc, operation="chat"
|
||||
)
|
||||
raise
|
||||
except asyncio.CancelledError:
|
||||
# 尽力而为且**取消优先**: 不 shield、不开后台任务;写入那一次 await 上
|
||||
# 再被取消则 `CancelledError` 照常传播(与 TelemetryMW 历史行为同款)
|
||||
await emit_terminal_once(
|
||||
self._emitter,
|
||||
request=request,
|
||||
context=context,
|
||||
error="cancelled",
|
||||
operation="chat",
|
||||
)
|
||||
raise
|
||||
# 快照在返回前冻结: 故它含缓存命中路径与已完成的内联遥测耗时
|
||||
return dataclasses.replace(response, call_stats=context.snapshot())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user