provider 的 HTTP 门面——仅非流式请求/响应。传输在 im.ttalk.http(clj-http,
designs/http-module.md);本 ns 只留 provider 的约定:JSON 编解码、{:status 0 :error …}(IO 失败不抛)、
:success?、canonical error 映射(response->error / success-body / check-model!)。
公开函数的签名与返回形状自 0.4.0 起未变。
流式(SSE)已迁移到 im.ttalk.agent.provider.http.stream-client(java.net.http 真增量,
见 docs/streaming-async-design.md);本模块不再提供流式 API。
用法: (require '[im.ttalk.agent.provider.http.client :as http]) (http/get "https://api.example.com/users") (http/post "https://api.example.com/users" :body {:name "张三"}) (http/post-deferred url :body {...}) ; 异步:返回 deferred<响应 map> (http/post-async url callback :body {...}) ; 异步回调(低层,形状同上) (http/request :get url :async? true :callback (fn [resp] ...))
provider 的 HTTP 门面——**仅非流式**请求/响应。传输在 `im.ttalk.http`(clj-http,
designs/http-module.md);本 ns 只留 provider 的约定:JSON 编解码、`{:status 0 :error …}`(IO 失败不抛)、
`:success?`、canonical error 映射(`response->error` / `success-body` / `check-model!`)。
公开函数的签名与返回形状自 0.4.0 起未变。
流式(SSE)已迁移到 `im.ttalk.agent.provider.http.stream-client`(java.net.http 真增量,
见 docs/streaming-async-design.md);本模块不再提供流式 API。
用法:
(require '[im.ttalk.agent.provider.http.client :as http])
(http/get "https://api.example.com/users")
(http/post "https://api.example.com/users" :body {:name "张三"})
(http/post-deferred url :body {...}) ; 异步:返回 deferred<响应 map>
(http/post-async url callback :body {...}) ; 异步回调(低层,形状同上)
(http/request :get url :async? true :callback (fn [resp] ...))HTTP 重试与错误分类(对标 Spring AI RetryUtils)
可重试(transient):
不可重试(non-transient):
(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)即等价于直接调用
provider 的真流式 —— 传输是 im.ttalk.http/stream-lines(clj-http,designs/http-module.md;
最初为替代 http-kit 的伪流式而写,修 BUG2)。本 ns 只留 provider 语义:parse-fn / process-fn、canonical error
(超时 / 非 2xx / IO)、模型名核对、限流头、重试与取消登记。
与 http-kit 客户端的根本区别:http-kit 的 :as :stream 等整段响应收完才兑现
promise(伪流式);stream-lines 用 BodyHandlers/fromLineSubscriber(响应式 Flow.Subscriber),
每行 SSE 到达即回调——真增量、非阻塞、可取消、原生背压。
与现有 stream 处理器无缝衔接:parse-fn / process-fn 复用 stream.anthropic / stream.openai 的纯函数(parse-sse-line / process-event),传输层只负责把行喂进来。
回调契约(与 http-kit 版 post-stream-async 一致):
返回 {:future CompletableFuture :cancel (fn [])}:
provider 的真流式 —— 传输是 `im.ttalk.http/stream-lines`(clj-http,designs/http-module.md;
最初为替代 http-kit 的伪流式而写,修 BUG2)。本 ns 只留 provider 语义:parse-fn / process-fn、canonical error
(超时 / 非 2xx / IO)、模型名核对、限流头、重试与取消登记。
与 http-kit 客户端的根本区别:http-kit 的 `:as :stream` 等整段响应收完才兑现
promise(伪流式);`stream-lines` 用 `BodyHandlers/fromLineSubscriber`(响应式 Flow.Subscriber),
**每行 SSE 到达即回调**——真增量、非阻塞、可取消、原生背压。
与现有 stream 处理器无缝衔接:parse-fn / process-fn 复用 stream.anthropic / stream.openai
的纯函数(parse-sse-line / process-event),传输层只负责把行喂进来。
回调契约(与 http-kit 版 post-stream-async 一致):
- :on-token (fn [token-data] ...) 每个 token/事件产出
- :on-complete (fn [final-state] ...) 流正常结束
- :on-error (fn [{:keys [error cause status]}] ...) 连接/解析/非 2xx 失败
返回 {:future CompletableFuture :cancel (fn [])}:
- future 在流结束(或失败)时完成;
- cancel 取消上游 HTTP(客户端断连/超时/用户停止时调用,不再消耗 token)。cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |