Liking cljdoc? Tell your friends :D

im.ttalk.agent.model.response

LLM 响应协议和类型定义

定义 Core 工具调用循环所需的响应接口契约。 第三方只需依赖 clj-agent-core 即可实现自定义响应类型。

核心概念:

  • ILLMResponse: 响应协议,定义 Core 所需的方法
  • ChatResponse: 默认实现,使用 record
  • make-response: 工厂函数,创建规范化的响应

使用示例:

(require '[im.ttalk.agent.model.response :as resp])

;; 创建响应 (def r (resp/make-response :text "你好" :tool-calls [{:id "1" :name "calc" :args {:x 1}}] :usage {:input_tokens 100 :output_tokens 50}))

;; 使用协议方法 (resp/response-text r) ; => "你好" (resp/has-tool-calls? r) ; => true (resp/response-tool-calls r) ; => [{:id "1" ...}]

LLM 响应协议和类型定义

定义 Core 工具调用循环所需的响应接口契约。
第三方只需依赖 clj-agent-core 即可实现自定义响应类型。

核心概念:
- ILLMResponse: 响应协议,定义 Core 所需的方法
- ChatResponse: 默认实现,使用 record
- make-response: 工厂函数,创建规范化的响应

使用示例:

(require '[im.ttalk.agent.model.response :as resp])

;; 创建响应
(def r (resp/make-response
         :text "你好"
         :tool-calls [{:id "1" :name "calc" :args {:x 1}}]
         :usage {:input_tokens 100 :output_tokens 50}))

;; 使用协议方法
(resp/response-text r)        ; => "你好"
(resp/has-tool-calls? r)      ; => true
(resp/response-tool-calls r)  ; => [{:id "1" ...}]
raw docstring

empty-completion?clj

(empty-completion? resp)

整个补全是不是空的(designs/provider-reliability.md §2,Keelson RetryingChatClient.IsEmptyCompletion)。

只认真正会进历史的几类为「有内容」:非空白文本、非空白推理、工具调用、回放块 (thinking / 服务端工具块)、原始响应里的 audio。usage / finish-reason 不是回答。 收尾原因是厂商结论(content-filter / max-tokens / pause / refusal)的不算空。

整个补全是不是空的(designs/provider-reliability.md §2,Keelson `RetryingChatClient.IsEmptyCompletion`)。

只认真正会进历史的几类为「有内容」:非空白文本、非空白推理、工具调用、回放块
(thinking / 服务端工具块)、原始响应里的 audio。usage / finish-reason 不是回答。
收尾原因是厂商结论(content-filter / max-tokens / pause / refusal)的不算空。
sourceraw docstring

extract-reasoningclj

(extract-reasoning raw)

从原始 API 响应提取推理/思考内容(与最终答案分离)

兼容两种形态:

  • Anthropic / Anthropic 兼容:content 中 type="thinking" 的块,取 :thinking 文本
  • OpenAI 兼容(DeepSeek-reasoner 等):choices[0].message.reasoning_content

返回:推理文本字符串,或 nil(普通模型 / 无推理内容)。

示例: (extract-reasoning {:content [{:type "thinking" :thinking "让我想想..."} {:type "text" :text "答案"}]}) ; => "让我想想..." (extract-reasoning {:choices [{:message {:reasoning_content "推理..." :content "答案"}}]}) ; => "推理..."

从原始 API 响应提取推理/思考内容(与最终答案分离)

兼容两种形态:
- Anthropic / Anthropic 兼容:content 中 type="thinking" 的块,取 :thinking 文本
- OpenAI 兼容(DeepSeek-reasoner 等):choices[0].message.reasoning_content

返回:推理文本字符串,或 nil(普通模型 / 无推理内容)。

示例:
(extract-reasoning {:content [{:type "thinking" :thinking "让我想想..."}
                              {:type "text" :text "答案"}]})
; => "让我想想..."
(extract-reasoning {:choices [{:message {:reasoning_content "推理..." :content "答案"}}]})
; => "推理..."
sourceraw docstring

get-input-tokensclj

(get-input-tokens resp)

获取输入 token 数量

获取输入 token 数量
sourceraw docstring

get-output-tokensclj

(get-output-tokens resp)

获取输出 token 数量

获取输出 token 数量
sourceraw docstring

get-total-tokensclj

(get-total-tokens resp)

获取总 token 数量

获取总 token 数量
sourceraw docstring

has-text?clj

(has-text? resp)

检查响应是否包含非空文本

参数:

  • resp: ILLMResponse 实例

返回: boolean

检查响应是否包含非空文本

参数:
- resp: ILLMResponse 实例

返回:
boolean
sourceraw docstring

ILLMResponsecljprotocol

LLM 响应统一接口

定义 Core 工具调用循环所需的契约。 所有 LLM 服务返回的响应都应实现此协议。

核心方法(Core 必需):

  • response-text 获取文本内容
  • response-tool-calls 获取工具调用列表
  • has-tool-calls? 是否包含工具调用

扩展方法:

  • response-usage Token 使用情况
  • response-finish-reason 完成原因
  • response-raw 原始响应
LLM 响应统一接口

定义 Core 工具调用循环所需的契约。
所有 LLM 服务返回的响应都应实现此协议。

核心方法(Core 必需):
- response-text        获取文本内容
- response-tool-calls  获取工具调用列表
- has-tool-calls?      是否包含工具调用

扩展方法:
- response-usage         Token 使用情况
- response-finish-reason 完成原因
- response-raw           原始响应

response-idclj

(response-id this)

获取响应 ID

返回:字符串或 nil

获取响应 ID

返回:字符串或 nil

response-modelclj

(response-model this)

获取模型名称

返回:字符串或 nil

获取模型名称

返回:字符串或 nil

response-rawclj

(response-raw this)

获取原始 API 响应

返回:原始响应数据

获取原始 API 响应

返回:原始响应数据

response-tool-callsclj

(response-tool-calls this)

获取工具调用列表

返回:工具调用列表 [{:id "..." :name "字符串" :args {...}}] 或 nil

获取工具调用列表

返回:工具调用列表 [{:id "..." :name "字符串" :args {...}}] 或 nil

response-usageclj

(response-usage this)

获取 Token 使用情况

返回:{:input-tokens n :output-tokens m :total-tokens t}

获取 Token 使用情况

返回:{:input-tokens n :output-tokens m :total-tokens t}

response-finish-reasonclj

(response-finish-reason this)

获取完成原因

返回::stop | :tool-use | :max-tokens | :content-filter | :unknown

获取完成原因

返回::stop | :tool-use | :max-tokens | :content-filter | :unknown

response-providerclj

(response-provider this)

获取 Provider 类型

返回:关键字 :openai | :anthropic | ...

获取 Provider 类型

返回:关键字 :openai | :anthropic | ...

response-textclj

(response-text this)

获取文本内容

返回:字符串或 nil

获取文本内容

返回:字符串或 nil

has-tool-calls?clj

(has-tool-calls? this)

是否包含工具调用

返回:boolean

是否包含工具调用

返回:boolean
sourceraw docstring

make-responseclj

(make-response &
               {:keys [text reasoning tool-calls usage finish-reason id model
                       provider raw-response replay-blocks]})

创建 LLM 响应

自动归一化 usage 和 finish-reason。

参数(关键字参数):

  • :text 文本内容
  • :tool-calls 工具调用列表
  • :usage Token 使用情况(自动归一化)
  • :finish-reason 完成原因(自动归一化)
  • :id 响应 ID
  • :model 模型名称
  • :provider Provider 类型
  • :raw-response 原始响应
  • :replay-blocks 需原样回放的不透明载荷 {:format kw :data ...}(可选,见 im.ttalk.agent.model/IReplayableResponse)

返回: ChatResponse record

示例: (make-response :text "你好" :tool-calls nil :usage {:prompt_tokens 100 :completion_tokens 50} :finish-reason "stop")

创建 LLM 响应

自动归一化 usage 和 finish-reason。

参数(关键字参数):
- :text           文本内容
- :tool-calls     工具调用列表
- :usage          Token 使用情况(自动归一化)
- :finish-reason  完成原因(自动归一化)
- :id             响应 ID
- :model          模型名称
- :provider       Provider 类型
- :raw-response   原始响应
- :replay-blocks  需原样回放的不透明载荷 {:format kw :data ...}(可选,见
                  im.ttalk.agent.model/IReplayableResponse)

返回:
ChatResponse record

示例:
(make-response
  :text "你好"
  :tool-calls nil
  :usage {:prompt_tokens 100 :completion_tokens 50}
  :finish-reason "stop")
sourceraw docstring

normalize-finish-reasonclj

(normalize-finish-reason reason)

归一化完成原因

将不同 Provider 的完成原因统一为标准关键字。

参数:

  • reason: 原始完成原因(字符串或关键字)

返回: 标准化关键字 :stop | :tool-use | :max-tokens | :content-filter | :unknown

示例: (normalize-finish-reason "tool_calls") ; => :tool-use (normalize-finish-reason "end_turn") ; => :stop (normalize-finish-reason :tool_use) ; => :tool-use

归一化完成原因

将不同 Provider 的完成原因统一为标准关键字。

参数:
- reason: 原始完成原因(字符串或关键字)

返回:
标准化关键字 :stop | :tool-use | :max-tokens | :content-filter | :unknown

示例:
(normalize-finish-reason "tool_calls")  ; => :tool-use
(normalize-finish-reason "end_turn")    ; => :stop
(normalize-finish-reason :tool_use)       ; => :tool-use
sourceraw docstring

normalize-usageclj

(normalize-usage usage)

归一化 Token 使用情况

将 OpenAI/Anthropic 的不同命名统一为标准格式。

参数:

  • usage: 原始 usage map(可能来自不同 Provider)

返回: 统一格式 {:input-tokens n :output-tokens m :total-tokens t :cache-read-tokens r :cache-write-tokens w} (cache 字段仅在原始 usage 含相应数据时出现,向后兼容)

示例: ;; OpenAI 格式 (normalize-usage {:prompt_tokens 100 :completion_tokens 50 :total_tokens 150}) ; => {:input-tokens 100 :output-tokens 50 :total-tokens 150}

;; Anthropic 格式(含 prompt caching) (normalize-usage {:input_tokens 100 :output_tokens 50 :cache_read_input_tokens 800 :cache_creation_input_tokens 200}) ; => {:input-tokens 100 :output-tokens 50 :total-tokens 150 ; :cache-read-tokens 800 :cache-write-tokens 200}

归一化 Token 使用情况

将 OpenAI/Anthropic 的不同命名统一为标准格式。

参数:
- usage: 原始 usage map(可能来自不同 Provider)

返回:
统一格式 {:input-tokens n :output-tokens m :total-tokens t
         :cache-read-tokens r :cache-write-tokens w}
(cache 字段仅在原始 usage 含相应数据时出现,向后兼容)

示例:
;; OpenAI 格式
(normalize-usage {:prompt_tokens 100 :completion_tokens 50 :total_tokens 150})
; => {:input-tokens 100 :output-tokens 50 :total-tokens 150}

;; Anthropic 格式(含 prompt caching)
(normalize-usage {:input_tokens 100 :output_tokens 50
                  :cache_read_input_tokens 800 :cache_creation_input_tokens 200})
; => {:input-tokens 100 :output-tokens 50 :total-tokens 150
;     :cache-read-tokens 800 :cache-write-tokens 200}
sourceraw docstring

pause-turn-resumable?clj

(pause-turn-resumable? resp)
(pause-turn-resumable? resp last-iteration?)

pause_turn 能不能原样续跑(Keelson PauseTurn.CanResume):

  • finish-reason 是 :pause;
  • 没有要本地执行的工具调用(有的话走正常工具循环);
  • 有回放块(server_tool_use 等不透明块)——没有块就没有可原样接回历史的东西,续不了;
  • 不是最后一轮(last-iteration? 为真时不续,以 :pause 正常收尾)。

续跑做法(react 侧):把这条 assistant 消息(含 :blocks)原样追加到历史末尾,不加 user 消息, 再请求一次;计入迭代预算。见 designs/provider-reliability.md §10.4。

pause_turn 能不能原样续跑(Keelson `PauseTurn.CanResume`):

- finish-reason 是 :pause;
- 没有要本地执行的工具调用(有的话走正常工具循环);
- 有回放块(server_tool_use 等不透明块)——没有块就没有可原样接回历史的东西,续不了;
- 不是最后一轮(`last-iteration?` 为真时不续,以 :pause 正常收尾)。

续跑做法(react 侧):把这条 assistant 消息(含 :blocks)原样追加到历史末尾,**不加 user 消息**,
再请求一次;计入迭代预算。见 designs/provider-reliability.md §10.4。
sourceraw docstring

pause-turn?clj

(pause-turn? resp)

这一轮是不是被服务端暂停了(Anthropic stop_reason pause_turn → :pause)。

这一轮是不是被服务端暂停了(Anthropic stop_reason pause_turn → :pause)。
sourceraw docstring

response-attemptsclj

(response-attempts resp)

这条响应经 ChatModel 重试层一共试了几次(含第一次);没经过(FnChatModel 等)→ nil。

这条响应经 ChatModel 重试层一共试了几次(含第一次);没经过(FnChatModel 等)→ nil。
sourceraw docstring

response-rate-limitclj

(response-rate-limit resp)

这次响应抄下来的限流快照(OpenAI x-ratelimit-* / Anthropic anthropic-ratelimit-* 折成同一形状): {:requests-limit :requests-remaining :requests-reset :tokens-limit :tokens-remaining :tokens-reset}, 头缺哪格就没有哪格;provider 没给任何限流头 → nil。

这次响应抄下来的限流快照(OpenAI x-ratelimit-* / Anthropic anthropic-ratelimit-* 折成同一形状):
{:requests-limit :requests-remaining :requests-reset :tokens-limit :tokens-remaining :tokens-reset},
头缺哪格就没有哪格;provider 没给任何限流头 → nil。
sourceraw docstring

response-reasoningclj

(response-reasoning resp)

获取推理/思考内容(reasoning_content / thinking)

返回:字符串或 nil。普通模型为 nil;推理模型(deepseek-reasoner、 Claude thinking、MiniMax-M 系列等)返回独立于最终答案的推理过程文本。

获取推理/思考内容(reasoning_content / thinking)

返回:字符串或 nil。普通模型为 nil;推理模型(deepseek-reasoner、
Claude thinking、MiniMax-M 系列等)返回独立于最终答案的推理过程文本。
sourceraw docstring

response-replay-blocksclj

(response-replay-blocks resp)

获取「必须原样带回下一轮」的不透明载荷 {:format kw :data ...},无则 nil。

由 chat-model 归一化时经可选协议 im.ttalk.agent.model/IReplayableResponse 抽取; provider 不实现该协议即恒为 nil(原路径不变)。 下游:filter/memory 的 response->neutral 把它挂到中立消息的 :blocks。

获取「必须原样带回下一轮」的不透明载荷 {:format kw :data ...},无则 nil。

由 chat-model 归一化时经可选协议 im.ttalk.agent.model/IReplayableResponse 抽取;
provider 不实现该协议即恒为 nil(原路径不变)。
下游:filter/memory 的 response->neutral 把它挂到中立消息的 :blocks。
sourceraw docstring

response?clj

(response? x)

检查是否为 ILLMResponse 实现

参数:

  • x: 任意值

返回: boolean

检查是否为 ILLMResponse 实现

参数:
- x: 任意值

返回:
boolean
sourceraw docstring

stopped-by-max-tokens?clj

(stopped-by-max-tokens? resp)

检查是否因达到最大 token 而停止

检查是否因达到最大 token 而停止
sourceraw docstring

stopped-by-tool-use?clj

(stopped-by-tool-use? resp)

检查是否因工具调用而停止

检查是否因工具调用而停止
sourceraw docstring

stopped-normally?clj

(stopped-normally? resp)

检查是否正常停止

检查是否正常停止
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