fix(llm): call_id 移入重试循环,每 attempt 独立

消除重试时遥测主键冲突的根因。每次 attempt 独立记录,
parent_call_id 不受影响(循环外固定),更利于事后诊断重试轨迹。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-09 00:12:38 -04:00
parent 5a91f392f0
commit f733c13dd1
2 changed files with 8 additions and 9 deletions
+6 -7
View File
@@ -295,12 +295,10 @@ class GovernedLLMClient:
if self._breaker.is_open(self._provider, time.monotonic()):
raise CircuitOpenError(f"熔断器已开启,拒绝调用 provider={self._provider}")
# ② call_id 生成
call_id = str(uuid4())
# ③ 缓存查询(cache 为 None 时跳过)
# ② 缓存查询(cache 为 None 时跳过)— call_id 在缓存路径独立生成
cached = await self._cache.get(self._model, messages) if self._cache is not None else None
if cached is not None:
cache_call_id = str(uuid4())
response = LLMResponse(
content=cached.content,
thinking=cached.thinking,
@@ -312,10 +310,10 @@ class GovernedLLMClient:
ttft_ms=None,
max_inter_token_ms=None,
cache_hit=True,
call_id=call_id,
call_id=cache_call_id,
)
await self._telemetry.record_llm_call(
call_id=call_id,
call_id=cache_call_id,
parent_call_id=parent_call_id,
session_id=session_id,
model_name=self._model,
@@ -333,9 +331,10 @@ class GovernedLLMClient:
)
return response
# 重试循环 + 流式消费
# 重试循环 + 流式消费(每次 attempt 独立 call_id
last_exc: Exception | None = None
for attempt in range(self._max_retries):
call_id = str(uuid4())
attempt_start = time.monotonic()
try:
content, thinking_text, ttft_ms, max_itoken_ms, usage = await self._call_streaming(
+2 -2
View File
@@ -197,9 +197,9 @@ async def test_transient_error_retries_and_records_telemetry():
success_calls = [c for c in telemetry.calls if c.get("error") is None]
assert len(success_calls) == 1
# 所有遥测记录应使用同一个 call_id(Important 2 修复验证
# 每次 attempt 应使用独立的 call_id(根因修复:防遥测主键冲突
call_ids = {c["call_id"] for c in telemetry.calls}
assert len(call_ids) == 1
assert len(call_ids) == 3 # 2 次失败 + 1 次成功 = 3 个独立 call_id
@pytest.mark.asyncio