defmodule ElixirAiWeb.ChatMessage do
use Phoenix.Component
alias Phoenix.LiveView.JS
import ElixirAiWeb.JsonDisplay
defp max_width_class, do: "max-w-full xl:max-w-300"
attr :content, :string, required: true
attr :tool_call_id, :string, required: true
def tool_result_message(assigns) do
~H"""
<%= if @reasoning_content && @reasoning_content != "" do %>
<% end %>
<%= for tool_call <- @tool_calls do %>
<.tool_call_item tool_call={tool_call} />
<% end %>
"""
end
attr :content, :string, required: true
attr :reasoning_content, :string, default: nil
attr :tool_calls, :list, default: []
attr :reasoning_id, :string, required: true
attr :expanded, :boolean, default: false
defp message_bubble(assigns) do
~H"""
<%= if @reasoning_content && @reasoning_content != "" do %>
<% end %>
<%= for tool_call <- @tool_calls do %>
<.tool_call_item tool_call={tool_call} />
<% end %>
<%= if @content && @content != "" do %>
<% end %>
"""
end
# Dispatches to the unified tool_call_card component, determining state from the map keys:
# :error key → :error (runtime failure)
# :result key → :success (completed)
# :index key → :pending (streaming in-progress)
# none → :called (DB-loaded; result is a separate message)
attr :tool_call, :map, required: true
defp tool_call_item(%{tool_call: tool_call} = assigns) do
state =
cond do
Map.has_key?(tool_call, :error) -> :error
Map.has_key?(tool_call, :result) -> :success
Map.has_key?(tool_call, :index) -> :pending
true -> :called
end
assigns =
assigns
|> assign(:_state, state)
|> assign(:_name, tool_call.name)
|> assign(:_arguments, tool_call[:arguments])
|> assign(:_result, tool_call[:result])
|> assign(:_error, tool_call[:error])
~H"<.tool_call_card
state={@_state}
name={@_name}
arguments={@_arguments}
result={@_result}
error={@_error}
/>"
end
attr :state, :atom, required: true
attr :name, :string, required: true
attr :arguments, :any, default: nil
attr :result, :any, default: nil
attr :error, :string, default: nil
defp tool_call_card(assigns) do
assigns =
assigns
|> assign(:_id, "tc-#{:erlang.phash2({assigns.name, assigns.arguments})}")
|> assign(:_truncated, truncate_args(assigns.arguments))
|> assign(
:_result_str,
case assigns.result do
nil -> nil
s when is_binary(s) -> s
other -> inspect(other, pretty: true, limit: :infinity)
end
)
~H"""