more test support
Some checks failed
CI/CD Pipeline / build (push) Failing after 4s

This commit is contained in:
2026-03-13 15:46:14 -06:00
parent 927c19dd17
commit 4de7db6f56
5 changed files with 44 additions and 13 deletions

View File

@@ -29,7 +29,7 @@ defmodule ElixirAi.Application do
delta_crdt_options: [sync_interval: 100], delta_crdt_options: [sync_interval: 100],
process_redistribution: :active process_redistribution: :active
]}, ]},
ElixirAi.ClusterSingleton cluster_singleton_child_spec()
] ]
opts = [strategy: :one_for_one, name: ElixirAi.Supervisor] opts = [strategy: :one_for_one, name: ElixirAi.Supervisor]
@@ -58,4 +58,12 @@ defmodule ElixirAi.Application do
{Task, fn -> ElixirAi.AiProvider.ensure_default_provider() end} {Task, fn -> ElixirAi.AiProvider.ensure_default_provider() end}
end end
end end
defp cluster_singleton_child_spec do
if Application.get_env(:elixir_ai, :env) == :test do
Supervisor.child_spec({Task, fn -> :ok end}, id: :skip_cluster_singleton)
else
ElixirAi.ClusterSingleton
end
end
end end

View File

@@ -25,10 +25,15 @@ defmodule ElixirAi.ChatRunner do
end end
def init(name) do def init(name) do
Phoenix.PubSub.subscribe(ElixirAi.PubSub, conversation_message_topic(name))
messages = messages =
case Conversation.find_id(name) do case Conversation.find_id(name) do
{:ok, conv_id} -> Message.load_for_conversation(conv_id, topic: conversation_message_topic(name)) {:ok, conv_id} ->
_ -> [] Message.load_for_conversation(conv_id, topic: conversation_message_topic(name))
_ ->
[]
end end
last_message = List.last(messages) last_message = List.last(messages)
@@ -106,6 +111,16 @@ defmodule ElixirAi.ChatRunner do
{:noreply, new_state} {:noreply, new_state}
end end
@ai_stream_events [
:ai_text_chunk,
:ai_reasoning_chunk,
:ai_text_stream_finish,
:ai_tool_call_start,
:ai_tool_call_middle,
:ai_tool_call_end,
:tool_response
]
def handle_info({:start_new_ai_response, id}, state) do def handle_info({:start_new_ai_response, id}, state) do
starting_response = %{id: id, reasoning_content: "", content: "", tool_calls: []} starting_response = %{id: id, reasoning_content: "", content: "", tool_calls: []}
broadcast_ui(state.name, {:start_ai_response_stream, starting_response}) broadcast_ui(state.name, {:start_ai_response_stream, starting_response})
@@ -117,9 +132,10 @@ defmodule ElixirAi.ChatRunner do
msg, msg,
%{streaming_response: %{id: current_id}} = state %{streaming_response: %{id: current_id}} = state
) )
when is_tuple(msg) and tuple_size(msg) in [2, 3] and elem(msg, 1) != current_id do when is_tuple(msg) and tuple_size(msg) in [2, 3] and
elem(msg, 0) in @ai_stream_events and elem(msg, 1) != current_id do
Logger.warning( Logger.warning(
"Received #{elem(msg, 0)} for id #{elem(msg, 1)} but current streaming response is for id #{current_id}" "Received #{elem(msg, 0)} for id #{inspect(elem(msg, 1))} but current streaming response is for id #{inspect(current_id)}"
) )
{:noreply, state} {:noreply, state}
@@ -220,8 +236,6 @@ defmodule ElixirAi.ChatRunner do
end end
def handle_info({:ai_tool_call_end, id}, state) do def handle_info({:ai_tool_call_end, id}, state) do
# Logger.info("ending tool call with tools: #{inspect(state.streaming_response.tool_calls)}")
tool_request_message = %{ tool_request_message = %{
role: :assistant, role: :assistant,
content: state.streaming_response.content, content: state.streaming_response.content,
@@ -294,6 +308,15 @@ defmodule ElixirAi.ChatRunner do
}} }}
end end
def handle_info({:db_error, reason}, state) do
broadcast_ui(state.name, {:db_error, reason})
{:noreply, state}
end
def handle_info({:store_message, _name, _message}, state) do
{:noreply, state}
end
def handle_info({:ai_request_error, reason}, state) do def handle_info({:ai_request_error, reason}, state) do
Logger.error("AI request error: #{inspect(reason)}") Logger.error("AI request error: #{inspect(reason)}")
broadcast_ui(state.name, {:ai_request_error, reason}) broadcast_ui(state.name, {:ai_request_error, reason})
@@ -308,7 +331,8 @@ defmodule ElixirAi.ChatRunner do
{:reply, state.streaming_response, state} {:reply, state.streaming_response, state}
end end
defp broadcast_ui(name, msg), do: Phoenix.PubSub.broadcast(ElixirAi.PubSub, chat_topic(name), msg) defp broadcast_ui(name, msg),
do: Phoenix.PubSub.broadcast(ElixirAi.PubSub, chat_topic(name), msg)
defp store_message(name, messages) when is_list(messages) do defp store_message(name, messages) when is_list(messages) do
Enum.each(messages, &store_message(name, &1)) Enum.each(messages, &store_message(name, &1))

View File

@@ -21,7 +21,7 @@ defmodule ElixirAiWeb.AiProvidersLive do
"name" => "", "name" => "",
"model_name" => "", "model_name" => "",
"api_token" => "", "api_token" => "",
"completions_url" => "https://api.openai.com/v1/chat/completions" "completions_url" => ""
} }
end end
) )
@@ -130,7 +130,7 @@ defmodule ElixirAiWeb.AiProvidersLive do
"name" => "", "name" => "",
"model_name" => "", "model_name" => "",
"api_token" => "", "api_token" => "",
"completions_url" => "https://api.openai.com/v1/chat/completions" "completions_url" => ""
} }
) )
|> assign(error: nil)} |> assign(error: nil)}

View File

@@ -24,7 +24,6 @@ defmodule ElixirAiWeb.ChatLive do
{:ok, _pid} -> {:ok, _pid} ->
if connected?(socket) do if connected?(socket) do
Phoenix.PubSub.subscribe(ElixirAi.PubSub, chat_topic(name)) Phoenix.PubSub.subscribe(ElixirAi.PubSub, chat_topic(name))
Phoenix.PubSub.subscribe(ElixirAi.PubSub, conversation_message_topic(name))
end end
conversation = ChatRunner.get_conversation(name) conversation = ChatRunner.get_conversation(name)

View File

@@ -1,6 +1,6 @@
defmodule ElixirAiWeb.ChatLiveTest do defmodule ElixirAiWeb.ChatLiveTest do
use ElixirAiWeb.ConnCase, async: false use ElixirAiWeb.ConnCase, async: false
import ElixirAi.PubsubTopics, only: [conversation_message_topic: 1] import ElixirAi.PubsubTopics, only: [chat_topic: 1]
setup do setup do
stub(ElixirAi.ConversationManager, :open_conversation, fn _name -> {:ok, self()} end) stub(ElixirAi.ConversationManager, :open_conversation, fn _name -> {:ok, self()} end)
@@ -17,7 +17,7 @@ defmodule ElixirAiWeb.ChatLiveTest do
Phoenix.PubSub.broadcast( Phoenix.PubSub.broadcast(
ElixirAi.PubSub, ElixirAi.PubSub,
conversation_message_topic("test_conv"), chat_topic("test_conv"),
{:db_error, "unique constraint violated"} {:db_error, "unique constraint violated"}
) )