feat: step 6 first round

This commit is contained in:
2026-04-28 20:38:26 +02:00
parent 0f3c1e12c1
commit 0929a4e798
12 changed files with 3072 additions and 141 deletions

View File

@@ -521,11 +521,19 @@ defmodule BDS.AI do
with {:ok, response} <- runtime.generate(endpoint_with_model(endpoint, model), request, 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
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),
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(
@@ -575,7 +583,7 @@ defmodule BDS.AI do
})
end
defp execute_tool_calls(conversation_id, tool_calls, project_id) do
defp execute_tool_calls(conversation_id, tool_calls, project_id, opts) do
tool_messages =
Enum.map(tool_calls, fn tool_call ->
result = execute_tool(tool_call.name, tool_call.arguments || %{}, project_id)
@@ -589,6 +597,8 @@ defmodule BDS.AI do
created_at: Persistence.now_ms()
})
notify_chat_event(opts, {:chat_tool_result, conversation_id, tool_call.name})
format_chat_message(message)
end)
@@ -652,7 +662,51 @@ defmodule BDS.AI do
%{
type: "form",
title: arguments["title"],
fields: arguments["fields"] || []
fields: arguments["fields"] || [],
submit_label: arguments["submit_label"] || arguments["submitLabel"],
submit_action: arguments["submit_action"] || arguments["submitAction"]
}
end
defp execute_tool("render_card", arguments, _project_id) do
%{
type: "card",
title: arguments["title"],
subtitle: arguments["subtitle"],
body: arguments["body"],
actions: arguments["actions"] || []
}
end
defp execute_tool("render_metric", arguments, _project_id) do
%{
type: "metric",
label: arguments["label"],
value: arguments["value"]
}
end
defp execute_tool("render_list", arguments, _project_id) do
%{
type: "list",
title: arguments["title"],
items: arguments["items"] || []
}
end
defp execute_tool("render_tabs", arguments, _project_id) do
%{
type: "tabs",
title: arguments["title"],
tabs: arguments["tabs"] || []
}
end
defp execute_tool("render_mindmap", arguments, _project_id) do
%{
type: "mindmap",
title: arguments["title"],
nodes: arguments["nodes"] || []
}
end
@@ -736,9 +790,14 @@ defmodule BDS.AI do
project_tools ++
[
%{name: "render_card", spec: tool_spec("render_card", "Return a structured card payload", render_card_schema())},
%{name: "render_table", spec: tool_spec("render_table", "Return a structured table payload", render_table_schema())},
%{name: "render_chart", spec: tool_spec("render_chart", "Return a structured chart payload", render_chart_schema())},
%{name: "render_form", spec: tool_spec("render_form", "Return a structured form payload", render_form_schema())}
%{name: "render_form", spec: tool_spec("render_form", "Return a structured form payload", render_form_schema())},
%{name: "render_metric", spec: tool_spec("render_metric", "Return a structured metric payload", render_metric_schema())},
%{name: "render_list", spec: tool_spec("render_list", "Return a structured list payload", render_list_schema())},
%{name: "render_tabs", spec: tool_spec("render_tabs", "Return a structured tabs payload", render_tabs_schema())},
%{name: "render_mindmap", spec: tool_spec("render_mindmap", "Return a structured mindmap payload", render_mindmap_schema())}
]
else
[]
@@ -1162,7 +1221,61 @@ defmodule BDS.AI do
"type" => "object",
"properties" => %{
"title" => %{"type" => "string"},
"fields" => %{"type" => "array"}
"fields" => %{"type" => "array"},
"submitLabel" => %{"type" => "string"},
"submitAction" => %{"type" => "string"}
}
}
end
defp render_card_schema do
%{
"type" => "object",
"properties" => %{
"title" => %{"type" => "string"},
"subtitle" => %{"type" => "string"},
"body" => %{"type" => "string"},
"actions" => %{"type" => "array"}
}
}
end
defp render_metric_schema do
%{
"type" => "object",
"properties" => %{
"label" => %{"type" => "string"},
"value" => %{"type" => "string"}
}
}
end
defp render_list_schema do
%{
"type" => "object",
"properties" => %{
"title" => %{"type" => "string"},
"items" => %{"type" => "array"}
}
}
end
defp render_tabs_schema do
%{
"type" => "object",
"properties" => %{
"title" => %{"type" => "string"},
"tabs" => %{"type" => "array"}
}
}
end
defp render_mindmap_schema do
%{
"type" => "object",
"properties" => %{
"title" => %{"type" => "string"},
"nodes" => %{"type" => "array"}
}
}
end
@@ -1170,6 +1283,16 @@ defmodule BDS.AI do
defp normalize_limit(value) when is_integer(value) and value > 0 and value <= 50, do: value
defp normalize_limit(_value), do: 10
defp notify_chat_event(opts, event) do
case Keyword.get(opts, :event_target) do
pid when is_pid(pid) -> send(pid, event)
callback when is_function(callback, 1) -> callback.(event)
_other -> :ok
end
:ok
end
defp truncate_text(nil, _max_length), do: ""
defp truncate_text(text, max_length) when is_binary(text) do