fix: ignore non-finite Retry-After hints
A gateway that answers 429 with Retry-After: inf (or 1e999, which float() happily rounds to inf) used to reach backoff_delay as retry_after_s=inf. max(delay, retry_after) then picked it, and since the library deliberately does not clamp the hint with backoff_max_s, the attempt slept forever. - _parse_retry_after now rejects non-finite values via math.isinf and returns None, so the call falls back to plain exponential backoff - it emits exactly one warning carrying the source name and the verdict word retry_after_not_finite, never the raw header: under a 429 storm an echoed header drowns the real signal and does not help localisation - source_name becomes a required keyword-only argument; the function is private, so no default is given and a missed call site fails loudly instead of silently dropping the source identity from the warning - nan keeps flowing through the existing seconds > 0 semantics; no new branch, no reordering of the parse
This commit is contained in:
@@ -9,6 +9,7 @@ SSE 纯函数移植 VT `adapters/llm.py:51-124`;错误翻译移植 CHS
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import time
|
||||
from dataclasses import replace
|
||||
@@ -108,14 +109,30 @@ async def _iter_sse_deltas(
|
||||
# —— 错误翻译(CHS invokers.py 同款)——
|
||||
|
||||
|
||||
def _parse_retry_after(raw: str | None) -> float | None:
|
||||
"""解析 Retry-After 头;仅支持秒数形态,HTTP-date 返回 None(CHS 同款)。"""
|
||||
def _parse_retry_after(raw: str | None, *, source_name: str) -> float | None:
|
||||
"""解析 Retry-After 头;仅支持秒数形态,HTTP-date 返回 None(CHS 同款)。
|
||||
|
||||
**非有限值必须当作"无提示"**(issue F1): `"inf"` / `"1e999"` 能被 `float()`
|
||||
成功解析,又能通过 `seconds > 0`,于是一路变成 `retry_after_s=inf`——而
|
||||
`backoff_delay` 的 `max(delay, retry_after)` 取大之后就是一次**永不醒来**的
|
||||
退避 sleep(库刻意不拿 `backoff_max_s` 去夹它,见设计 §6.2)。
|
||||
|
||||
`source_name` 是**必填** keyword-only 参数: 本函数是私有的,不给默认值,
|
||||
漏传即 `TypeError`,免得将来新增调用点静默丢掉源标识(告警定位不到是哪个源)。
|
||||
`nan` 不新增分支,沿用既有 `seconds > 0` 恒假的值语义。
|
||||
"""
|
||||
if raw is None:
|
||||
return None
|
||||
try:
|
||||
seconds = float(raw.strip())
|
||||
except ValueError:
|
||||
return None
|
||||
if math.isinf(seconds):
|
||||
# 只写源名与判据词: 429 风暴下回显原始头会把日志淹掉,也无助于定位
|
||||
logger.warning(
|
||||
"{} 的 Retry-After 非有限值,按无提示处理(retry_after_not_finite)", source_name
|
||||
)
|
||||
return None
|
||||
return seconds if seconds > 0 else None
|
||||
|
||||
|
||||
@@ -137,7 +154,7 @@ def _translate_429(
|
||||
)
|
||||
return TransientError(
|
||||
compose_message(f"{source.name} 限速: 429", summary),
|
||||
retry_after_s=_parse_retry_after(headers.get("retry-after")),
|
||||
retry_after_s=_parse_retry_after(headers.get("retry-after"), source_name=source.name),
|
||||
**ctx,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user