Liking cljdoc? Tell your friends :D

im.ttalk.agent.provider.http.retry

HTTP 重试与错误分类(对标 Spring AI RetryUtils)

======================================== 设计目标

  • 错误二分类:transient(可重试)vs non-transient(不可重试),对标 Spring AI 的 TransientAiException / NonTransientAiException。
  • 指数退避 + 满抖动(full jitter),避免重试风暴。
  • 尊重服务端的 Retry-After 响应头(429 / 503 常见)。
  • opt-in:仅当调用方在 config 里传入 :retry 时启用,不改变现有默认行为。

======================================== 错误分类规则

可重试(transient):

  • 网络层错误(连接失败、超时)—— HTTP 客户端返回 {:error ...}
  • HTTP 408 / 409 / 425 / 429 / 500 / 502 / 503 / 504 / 529

不可重试(non-transient):

  • 其余 4xx(400 参数错误、401 鉴权、403、404 等)—— 重试无意义

======================================== 基本用法

(require '[im.ttalk.agent.provider.http.retry :as retry])

;; 包裹一个返回 http response map 的函数 (retry/with-retry #(http/post url :headers h :body b) {:max-retries 3})

;; 关闭重试(max-retries 0)即等价于直接调用

HTTP 重试与错误分类(对标 Spring AI RetryUtils)

========================================
设计目标
========================================

- 错误二分类:transient(可重试)vs non-transient(不可重试),对标
  Spring AI 的 TransientAiException / NonTransientAiException。
- 指数退避 + 满抖动(full jitter),避免重试风暴。
- 尊重服务端的 Retry-After 响应头(429 / 503 常见)。
- opt-in:仅当调用方在 config 里传入 :retry 时启用,不改变现有默认行为。

========================================
错误分类规则
========================================

可重试(transient):
 - 网络层错误(连接失败、超时)—— HTTP 客户端返回 {:error ...}
- HTTP 408 / 409 / 425 / 429 / 500 / 502 / 503 / 504 / 529

不可重试(non-transient):
- 其余 4xx(400 参数错误、401 鉴权、403、404 等)—— 重试无意义

========================================
基本用法
========================================

(require '[im.ttalk.agent.provider.http.retry :as retry])

;; 包裹一个返回 http response map 的函数
(retry/with-retry
  #(http/post url :headers h :body b)
  {:max-retries 3})

;; 关闭重试(max-retries 0)即等价于直接调用
raw docstring

compute-backoffclj

(compute-backoff attempt opts)
(compute-backoff attempt opts rand-fn)

计算第 attempt 次重试的退避毫秒(含满抖动)——就是 im.ttalk.agent.retry/compute-backoff (ChatModel 层重试用的同一份;两份曾逐字重复,2026-11-09 收成一份,designs/http-module.md §8)。

attempt 从 0 开始。基础退避 = min(base * multiplier^attempt, max-delay), 再在 [0, 基础退避] 上取满抖动(full jitter)。

参数:

  • attempt: 第几次重试(0-based)
  • opts: {:base-delay :multiplier :max-delay}
  • rand-fn: 取 [0,1) 随机数的函数,便于测试注入(缺省 rand)
计算第 attempt 次重试的退避毫秒(含满抖动)——就是 `im.ttalk.agent.retry/compute-backoff`
(ChatModel 层重试用的同一份;两份曾逐字重复,2026-11-09 收成一份,designs/http-module.md §8)。

attempt 从 0 开始。基础退避 = min(base * multiplier^attempt, max-delay),
再在 [0, 基础退避] 上取满抖动(full jitter)。

参数:
- attempt: 第几次重试(0-based)
- opts:    {:base-delay :multiplier :max-delay}
- rand-fn: 取 [0,1) 随机数的函数,便于测试注入(缺省 rand)
sourceraw docstring

default-retry-optsclj

默认重试配置

  • :max-retries 最大重试次数(不含首次调用)
  • :base-delay 首次退避基准(毫秒)
  • :multiplier 退避倍率
  • :max-delay 单次退避上限(毫秒)
  • :respect-retry-after? 是否优先采用响应头 Retry-After
默认重试配置

- :max-retries          最大重试次数(不含首次调用)
- :base-delay           首次退避基准(毫秒)
- :multiplier           退避倍率
- :max-delay            单次退避上限(毫秒)
- :respect-retry-after? 是否优先采用响应头 Retry-After
sourceraw docstring

maybe-with-retryclj

(maybe-with-retry config request-fn)

根据 config :retry 决定是否启用重试(opt-in 入口)

参数:

  • config: provider config,读取其 :retry
    • 为 nil/false -> 直接调用 request-fn(零开销)
    • 为 true -> 用默认配置重试
    • 为 map -> 作为重试配置
  • request-fn: 无参函数,返回 http response map

返回: http response map

根据 config :retry 决定是否启用重试(opt-in 入口)

参数:
- config:     provider config,读取其 :retry
  - 为 nil/false -> 直接调用 request-fn(零开销)
  - 为 true      -> 用默认配置重试
  - 为 map       -> 作为重试配置
- request-fn: 无参函数,返回 http response map

返回: http response map
sourceraw docstring

parse-retry-afterclj

(parse-retry-after headers)
(parse-retry-after headers now-ms)

从响应头解析服务端要求的等待,返回毫秒数或 nil(不判上限,上限在 with-retry 里按 :max-delay 截)。

解析在 im.ttalk.http.date/retry-after-ms:retry-after-ms 头(毫秒)优先,其次 Retry-After—— 秒数(可带小数)或 HTTP 日期(IMF-fixdate / RFC 850 / asctime,距今毫秒,已过 ⇒ 0)。 头名大小写无关;值是 List<String> / 向量时取第一个——java.net.http 给的正是 List,此前直接 (str v) 得到 "[120]",Retry-After 在真实请求上从未生效(designs/provider-reliability.md §1.3)。

参数:

  • headers: 响应头 map
  • now-ms: 当前时间戳(毫秒),便于测试注入;缺省取系统时间
从响应头解析服务端要求的等待,返回毫秒数或 nil(不判上限,上限在 `with-retry` 里按 :max-delay 截)。

解析在 `im.ttalk.http.date/retry-after-ms`:`retry-after-ms` 头(毫秒)优先,其次 `Retry-After`——
秒数(可带小数)或 HTTP 日期(IMF-fixdate / RFC 850 / asctime,距今毫秒,已过 ⇒ 0)。
头名大小写无关;值是 `List<String>` / 向量时取第一个——java.net.http 给的正是 List,此前直接 `(str v)` 得到
"[120]",Retry-After 在真实请求上**从未生效**(designs/provider-reliability.md §1.3)。

参数:
- headers: 响应头 map
- now-ms:  当前时间戳(毫秒),便于测试注入;缺省取系统时间
sourceraw docstring

retryable-ex?clj

(retryable-ex? e)

异常是否可重试

连接/超时类异常视为可重试;其余默认不可重试。

异常是否可重试

连接/超时类异常视为可重试;其余默认不可重试。
sourceraw docstring

transient-response?clj

(transient-response? resp)

http response map 是否代表一个可重试的失败

参数:

  • resp: {:status int :error any :success? bool ...}

返回: boolean

规则:

  • 网络层错误 (:error 非空) -> 可重试
  • 状态码命中 transient-status-codes -> 可重试
  • 其余(含成功响应、不可重试 4xx)-> 不重试
http response map 是否代表一个可重试的失败

参数:
- resp: {:status int :error any :success? bool ...}

返回: boolean

规则:
- 网络层错误 (:error 非空) -> 可重试
- 状态码命中 transient-status-codes -> 可重试
- 其余(含成功响应、不可重试 4xx)-> 不重试
sourceraw docstring

transient-status-codesclj

可重试的 HTTP 状态码集合

可重试的 HTTP 状态码集合
sourceraw docstring

transient-status?clj

(transient-status? status)

状态码是否可重试

示例: (transient-status? 429) ; => true (transient-status? 400) ; => false

状态码是否可重试

示例:
(transient-status? 429) ; => true
(transient-status? 400) ; => false
sourceraw docstring

with-retryclj

(with-retry request-fn opts)

带指数退避的重试包装

参数:

  • request-fn: 无参函数,返回 http response map({:status :error :success? ...})
  • opts: 重试配置(合并 default-retry-opts)
    • :max-retries 最大重试次数
    • :base-delay 退避基准毫秒
    • :multiplier 退避倍率
    • :max-delay 退避上限毫秒
    • :respect-retry-after? 是否优先用 Retry-After 头
    • :retry-on 判定是否需要重试的谓词 (fn [resp] bool), 缺省 transient-response?
    • :rand-fn 随机数函数(测试用)
    • :on-retry 每次重试前回调 (fn [{:keys [attempt delay-ms response]}])

返回: 最终 http response map(成功,或重试耗尽后的最后一次响应)

说明:

  • 仅对同步、整体可重试的请求使用(如非流式 LLM 调用、或流式建链阶段)。
  • max-retries 为 0 时等价于直接调用 request-fn。
带指数退避的重试包装

参数:
- request-fn: 无参函数,返回 http response map({:status :error :success? ...})
- opts:       重试配置(合并 default-retry-opts)
  - :max-retries          最大重试次数
  - :base-delay           退避基准毫秒
  - :multiplier           退避倍率
  - :max-delay            退避上限毫秒
  - :respect-retry-after? 是否优先用 Retry-After 头
  - :retry-on             判定是否需要重试的谓词 (fn [resp] bool),
                          缺省 transient-response?
  - :rand-fn              随机数函数(测试用)
  - :on-retry             每次重试前回调 (fn [{:keys [attempt delay-ms response]}])

返回: 最终 http response map(成功,或重试耗尽后的最后一次响应)

说明:
- 仅对同步、整体可重试的请求使用(如非流式 LLM 调用、或流式建链阶段)。
- max-retries 为 0 时等价于直接调用 request-fn。
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close