defmodule ElixirAiWeb.ChatMessage do
use Phoenix.Component
alias ElixirAiWeb.Markdown
alias Phoenix.LiveView.JS
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 %>
JS.toggle_class("rotate-180", to: "##{@reasoning_id}-chevron")
}
aria-label="Toggle reasoning"
>
{Markdown.render(@reasoning_content)}
<% end %>
<%= for tool_call <- @tool_calls do %>
<.tool_call_item tool_call={tool_call} />
<% end %>
<%= if @content && @content != "" do %>
{Markdown.render(@content)}
<% end %>
"""
end
# Dispatches to the appropriate tool call component based on result state
attr :tool_call, :map, required: true
defp tool_call_item(%{tool_call: tool_call} = assigns) do
cond do
Map.has_key?(tool_call, :error) ->
assigns =
assigns
|> assign(:name, tool_call.name)
|> assign(:arguments, tool_call[:arguments] || "")
|> assign(:error, tool_call.error)
~H"<.error_tool_call name={@name} arguments={@arguments} error={@error} />"
Map.has_key?(tool_call, :result) ->
assigns =
assigns
|> assign(:name, tool_call.name)
|> assign(:arguments, tool_call[:arguments] || "")
|> assign(:result, tool_call.result)
~H"<.success_tool_call name={@name} arguments={@arguments} result={@result} />"
true ->
assigns =
assigns
|> assign(:name, tool_call.name)
|> assign(:arguments, tool_call[:arguments] || "")
~H"<.pending_tool_call name={@name} arguments={@arguments} />"
end
end
attr :name, :string, required: true
attr :arguments, :string, default: ""
defp pending_tool_call(assigns) do
~H"""
<.tool_call_icon />
{@name}
running
<.tool_call_args arguments={@arguments} />
"""
end
attr :name, :string, required: true
attr :arguments, :string, default: ""
attr :result, :any, required: true
defp success_tool_call(assigns) do
assigns =
assign(assigns, :result_str, case assigns.result do
s when is_binary(s) -> s
other -> inspect(other, pretty: true, limit: :infinity)
end)
~H"""
<.tool_call_icon />
{@name}
done
<.tool_call_args arguments={@arguments} />
"""
end
attr :name, :string, required: true
attr :arguments, :string, default: ""
attr :error, :string, required: true
defp error_tool_call(assigns) do
~H"""
<.tool_call_icon />
{@name}
error
<.tool_call_args arguments={@arguments} />
"""
end
attr :arguments, :string, default: ""
defp tool_call_args(%{arguments: args} = assigns) when args != "" do
assigns =
assign(assigns, :pretty_args, case Jason.decode(args) do
{:ok, decoded} -> Jason.encode!(decoded, pretty: true)
_ -> args
end)
~H"""