Packages

fnord

0.9.37

AI code archaeology

Current section

Files

Jump to
fnord lib ai message function_call_output.ex
Raw

lib/ai/message/function_call_output.ex

defmodule AI.Message.FunctionCallOutput do
@moduledoc """
Result of a tool execution, paired to a `FunctionCall` by `call_id`.
Wire shape:
%{type: "function_call_output", call_id: "...", output: "..."}
"""
@behaviour AI.Message
@derive {Jason.Encoder, only: [:type, :call_id, :output]}
@enforce_keys [:call_id, :output]
defstruct [:call_id, :output, type: "function_call_output"]
@type t :: %__MODULE__{
call_id: binary(),
output: binary()
}
@doc """
Build a function-call-output. `output` is coerced to a binary if a tool
returned something non-string (inspect/1 fallback) so the wire shape stays
consistent.
"""
@spec new(binary(), any()) :: t()
def new(call_id, output) when is_binary(call_id) do
%__MODULE__{call_id: call_id, output: stringify(output)}
end
@impl AI.Message
def text(%__MODULE__{output: output}), do: output
@impl AI.Message
def for_transcript(%__MODULE__{output: output}) do
"""
# TOOL OUTPUT
#{output}
"""
end
@impl AI.Message
def to_map(%__MODULE__{call_id: call_id, output: output}) do
%{type: "function_call_output", call_id: call_id, output: output}
end
@doc "Hydrate from a Responses-shaped map (atom or string keys)."
@spec from_map(map()) :: t()
def from_map(raw) do
call_id = AI.Message.get(raw, :call_id) || AI.Message.get(raw, :tool_call_id) || ""
output = stringify(AI.Message.get(raw, :output) || AI.Message.get(raw, :content) || "")
%__MODULE__{call_id: call_id, output: output}
end
defp stringify(s) when is_binary(s), do: s
defp stringify(other), do: inspect(other, pretty: true)
end