Current section
Files
Jump to
Current section
Files
lib/cmdc/provider/stream_bridge.ex
defmodule CMDC.Provider.StreamBridge do
@moduledoc """
将 req_llm 的 `StreamResponse` 转为 gen_statem 消息的桥接进程。
在独立 Task 中遍历 req_llm 的 Stream,将每个 `StreamChunk` 转为
`{:cmdc_stream_chunk, chunk}` 消息发送给 Agent 进程。
流结束后发送 `:cmdc_stream_done`,出错则发送 `{:cmdc_stream_error, reason}`。
## 消息格式
| 消息 | 触发时机 |
|------|---------|
| `{:cmdc_stream_chunk, %ReqLLM.StreamChunk{}}` | 每个 chunk 到达 |
| `:cmdc_stream_done` | 流正常结束 |
| `{:cmdc_stream_error, reason}` | 流出错 |
"""
require Logger
# ==========================================================================
# Public API
# ==========================================================================
@doc """
启动 StreamBridge 进程,消费 StreamResponse 并将 chunk 转发给 Agent。
返回桥接进程的 PID,Agent 可用于 stall 检测或强制终止。
"""
@spec start(ReqLLM.StreamResponse.t(), pid()) :: pid()
def start(%ReqLLM.StreamResponse{} = stream_response, agent_pid) when is_pid(agent_pid) do
spawn(fn -> consume_stream(stream_response, agent_pid) end)
end
# ==========================================================================
# 私有辅助
# ==========================================================================
defp consume_stream(stream_response, agent_pid) do
stream_response.stream
|> Stream.each(fn chunk ->
send(agent_pid, {:cmdc_stream_chunk, chunk})
end)
|> Stream.run()
send(agent_pid, :cmdc_stream_done)
rescue
error ->
Logger.error("[StreamBridge] 流式消费异常: #{inspect(error)}")
send(agent_pid, {:cmdc_stream_error, error})
catch
:exit, reason ->
Logger.error("[StreamBridge] 进程退出: #{inspect(reason)}")
send(agent_pid, {:cmdc_stream_error, {:exit, reason}})
end
end