From dc6fc4c6313607e61f8a58d0388f71fc584ab228 Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Sun, 21 Jun 2026 13:54:35 +0200 Subject: [PATCH] Simplify chat tool round flow --- AUDIT.md | 1 + lib/bds/ai/chat.ex | 112 +++++++++++++++++++++++++++++---------------- 2 files changed, 73 insertions(+), 40 deletions(-) diff --git a/AUDIT.md b/AUDIT.md index f9cf5b4..2fb99f5 100644 --- a/AUDIT.md +++ b/AUDIT.md @@ -29,6 +29,7 @@ after each item. - [x] `mcp/tools.ex` - [x] `mcp/server.ex` - [x] `ai/chat_tools.ex` +- [x] `ai/chat.ex` --- diff --git a/lib/bds/ai/chat.ex b/lib/bds/ai/chat.ex index a75fa78..26a9ddd 100644 --- a/lib/bds/ai/chat.ex +++ b/lib/bds/ai/chat.ex @@ -578,47 +578,22 @@ defmodule BDS.AI.Chat do runtime.generate(Runtime.endpoint_with_model(endpoint, model), request, generate_opts), {:ok, assistant_message} <- persist_assistant_response(conversation.id, response), :ok <- touch_conversation(conversation.id) do - if is_binary(Map.get(response, :content)) and String.trim(Map.get(response, :content)) != "" do - notify_chat_event( - opts, - {:chat_streaming_content, conversation.id, Map.get(response, :content)} - ) - end + notify_assistant_content(response, conversation.id, opts) - tool_calls = decode_tool_calls(Map.get(response, :tool_calls)) - - Enum.each(tool_calls, fn tool_call -> - notify_chat_event(opts, {:chat_tool_call, conversation.id, tool_call}) - end) - - cond do - tool_calls != [] and tools != [] -> - with {:ok, tool_messages} <- - execute_tool_calls(conversation.id, tool_calls, project_id, opts), - updated_messages <- load_chat_messages(conversation.id), - {:ok, reply} <- - chat_round( - Repo.get!(ChatConversation, conversation.id), - updated_messages, - endpoint, - model, - project_id, - tools, - runtime, - opts, - rounds_left - 1 - ) do - {:ok, Map.put(reply, :tool_messages, tool_messages)} - end - - true -> - {:ok, - %{ - conversation: format_conversation(Repo.get!(ChatConversation, conversation.id)), - assistant_message: format_chat_message(assistant_message), - tool_messages: [] - }} - end + response + |> Map.get(:tool_calls) + |> decode_tool_calls() + |> continue_chat_round( + conversation.id, + assistant_message, + endpoint, + model, + project_id, + tools, + runtime, + opts, + rounds_left + ) end end @@ -673,6 +648,63 @@ defmodule BDS.AI.Chat do {:ok, tool_messages} end + defp continue_chat_round( + tool_calls, + conversation_id, + assistant_message, + endpoint, + model, + project_id, + tools, + runtime, + opts, + rounds_left + ) do + Enum.each(tool_calls, fn tool_call -> + notify_chat_event(opts, {:chat_tool_call, conversation_id, tool_call}) + end) + + if tool_calls != [] and tools != [] do + with {:ok, tool_messages} <- execute_tool_calls(conversation_id, tool_calls, project_id, opts), + updated_messages <- load_chat_messages(conversation_id), + {:ok, reply} <- + chat_round( + Repo.get!(ChatConversation, conversation_id), + updated_messages, + endpoint, + model, + project_id, + tools, + runtime, + opts, + rounds_left - 1 + ) do + {:ok, Map.put(reply, :tool_messages, tool_messages)} + end + else + final_chat_reply(conversation_id, assistant_message) + end + end + + defp final_chat_reply(conversation_id, assistant_message) do + {:ok, + %{ + conversation: format_conversation(Repo.get!(ChatConversation, conversation_id)), + assistant_message: format_chat_message(assistant_message), + tool_messages: [] + }} + end + + defp notify_assistant_content(response, conversation_id, opts) do + content = Map.get(response, :content) + + if is_binary(content) and String.trim(content) != "" do + notify_chat_event(opts, {:chat_streaming_content, conversation_id, content}) + end + + :ok + end + defp build_chat_request(conversation, messages, model, project_id, tools) do system_message = %{"role" => "system", "content" => chat_system_prompt(project_id, tools)}