fix: more work on chat and chat titles

This commit is contained in:
2026-05-01 23:34:37 +02:00
parent c495a2ed0a
commit 7db8f6d36b
8 changed files with 296 additions and 22 deletions

View File

@@ -5,6 +5,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
import Phoenix.HTML, only: [raw: 1]
alias BDS.AI
alias BDS.MapUtils
alias BDS.Desktop.ShellData
alias BDS.Desktop.ShellLive.ChatEditor.{MessageBuild, ModelSelection, ToolTracking}
@@ -249,8 +250,10 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
)
case result do
{:ok, _reply} ->
reload.(socket, socket.assigns.workbench)
{:ok, reply} ->
socket
|> update_tab_meta_from_reply(conversation_id, reply)
|> reload.(socket.assigns.workbench)
{:error, :cancelled} ->
reload.(socket, socket.assigns.workbench)
@@ -266,6 +269,23 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
end
end
defp update_tab_meta_from_reply(socket, conversation_id, reply) do
title =
reply
|> MapUtils.attr(:conversation, %{})
|> MapUtils.attr(:title)
if is_binary(title) and String.trim(title) != "" do
key = {:chat, conversation_id}
assign(socket, :tab_meta, Map.update(socket.assigns.tab_meta, key, %{title: title}, fn meta ->
Map.put(meta, :title, title)
end))
else
socket
end
end
# ── HEEx-callable helpers ─────────────────────────────────────────────────
@spec message_role_label(term()) :: term()

View File

@@ -21,6 +21,10 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
model_supports_tool_calls?(
get_model_preference(:chat) || Map.get(online_endpoint || %{}, :model, "")
),
"online_chat_disable_reasoning" =>
model_disables_reasoning?(
get_model_preference(:chat) || Map.get(online_endpoint || %{}, :model, "")
),
"online_title_model" => get_model_preference(:title),
"online_image_analysis_model" => get_model_preference(:image_analysis),
"offline_url" => Map.get(airplane_endpoint || %{}, :url, ""),
@@ -33,6 +37,10 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
model_supports_tool_calls?(
get_model_preference(:airplane_chat) || Map.get(airplane_endpoint || %{}, :model, "")
),
"offline_chat_disable_reasoning" =>
model_disables_reasoning?(
get_model_preference(:airplane_chat) || Map.get(airplane_endpoint || %{}, :model, "")
),
"offline_title_model" => get_model_preference(:airplane_title),
"offline_image_analysis_model" => get_model_preference(:airplane_image_analysis),
"system_prompt" => EditorSettings.get_global_setting("ai.system_prompt") || ""
@@ -99,12 +107,20 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
:ok <- AI.set_airplane_mode(attrs.offline_mode),
:ok <- maybe_put_model_preference(:chat, attrs.online_chat_model),
:ok <-
maybe_put_chat_model_capabilities(attrs.online_chat_model, attrs.online_chat_tools),
maybe_put_chat_model_capabilities(
attrs.online_chat_model,
attrs.online_chat_tools,
attrs.online_chat_disable_reasoning
),
:ok <- maybe_put_model_preference(:title, attrs.online_title_model),
:ok <- maybe_put_model_preference(:image_analysis, attrs.online_image_analysis_model),
:ok <- maybe_put_model_preference(:airplane_chat, attrs.offline_chat_model),
:ok <-
maybe_put_chat_model_capabilities(attrs.offline_chat_model, attrs.offline_chat_tools),
maybe_put_chat_model_capabilities(
attrs.offline_chat_model,
attrs.offline_chat_tools,
attrs.offline_chat_disable_reasoning
),
:ok <- maybe_put_model_preference(:airplane_title, attrs.offline_title_model),
:ok <-
maybe_put_model_preference(
@@ -147,6 +163,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
online_api_key: blank_to_nil(Map.get(draft, "online_api_key")),
online_chat_model: blank_to_nil(Map.get(draft, "online_chat_model")),
online_chat_tools: truthy?(Map.get(draft, "online_chat_tools")),
online_chat_disable_reasoning: truthy?(Map.get(draft, "online_chat_disable_reasoning")),
online_title_model: blank_to_nil(Map.get(draft, "online_title_model")),
online_image_analysis_model: blank_to_nil(Map.get(draft, "online_image_analysis_model")),
offline_url: blank_to_nil(Map.get(draft, "offline_url")),
@@ -154,6 +171,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
offline_mode: truthy?(Map.get(draft, "offline_mode")),
offline_chat_model: blank_to_nil(Map.get(draft, "offline_chat_model")),
offline_chat_tools: truthy?(Map.get(draft, "offline_chat_tools")),
offline_chat_disable_reasoning: truthy?(Map.get(draft, "offline_chat_disable_reasoning")),
offline_title_model: blank_to_nil(Map.get(draft, "offline_title_model")),
offline_image_analysis_model: blank_to_nil(Map.get(draft, "offline_image_analysis_model")),
system_prompt: Map.get(draft, "system_prompt", "")
@@ -166,6 +184,8 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
"online_api_key" => Map.get(params, "online_api_key", ""),
"online_chat_model" => Map.get(params, "online_chat_model", ""),
"online_chat_tools" => truthy?(Map.get(params, "online_chat_tools")),
"online_chat_disable_reasoning" =>
truthy?(Map.get(params, "online_chat_disable_reasoning")),
"online_title_model" => Map.get(params, "online_title_model", ""),
"online_image_analysis_model" => Map.get(params, "online_image_analysis_model", ""),
"offline_url" => Map.get(params, "offline_url", ""),
@@ -173,6 +193,8 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
"offline_mode" => truthy?(Map.get(params, "offline_mode")),
"offline_chat_model" => Map.get(params, "offline_chat_model", ""),
"offline_chat_tools" => truthy?(Map.get(params, "offline_chat_tools")),
"offline_chat_disable_reasoning" =>
truthy?(Map.get(params, "offline_chat_disable_reasoning")),
"offline_title_model" => Map.get(params, "offline_title_model", ""),
"offline_image_analysis_model" => Map.get(params, "offline_image_analysis_model", ""),
"system_prompt" => Map.get(params, "system_prompt", "")
@@ -190,15 +212,16 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
defp maybe_put_model_preference(_key, ""), do: :ok
defp maybe_put_model_preference(key, value), do: AI.put_model_preference(key, value)
defp maybe_put_chat_model_capabilities(nil, _supports_tool_calls), do: :ok
defp maybe_put_chat_model_capabilities("", _supports_tool_calls), do: :ok
defp maybe_put_chat_model_capabilities(nil, _supports_tool_calls, _disables_reasoning), do: :ok
defp maybe_put_chat_model_capabilities("", _supports_tool_calls, _disables_reasoning), do: :ok
defp maybe_put_chat_model_capabilities(model, supports_tool_calls) do
defp maybe_put_chat_model_capabilities(model, supports_tool_calls, disables_reasoning) do
existing = BDS.AI.Catalog.model_capabilities(model)
AI.put_model_capabilities(model, %{
supports_attachment: existing.supports_attachment,
supports_tool_calls: supports_tool_calls
supports_tool_calls: supports_tool_calls,
disables_reasoning: disables_reasoning
})
end
@@ -208,6 +231,12 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
defp model_supports_tool_calls?(model),
do: BDS.AI.Catalog.model_capabilities(model).supports_tool_calls
defp model_disables_reasoning?(nil), do: false
defp model_disables_reasoning?(""), do: false
defp model_disables_reasoning?(model),
do: BDS.AI.Catalog.model_capabilities(model).disables_reasoning
defp put_endpoint_preferences(kind, url, api_key, primary_model) do
if Enum.all?([url, api_key, primary_model], &(blank_to_nil(&1) == nil)) do
AI.delete_endpoint(kind)

View File

@@ -233,6 +233,10 @@
<div class="setting-info"><label class="setting-label"><%= translated("Online Chat Tools") %></label></div>
<div class="setting-control"><label><input type="checkbox" name="settings_ai[online_chat_tools]" checked={@settings_editor.ai["online_chat_tools"]} /> <%= translated("Enable tool calls for the online chat model") %></label></div>
</div>
<div class="setting-row">
<div class="setting-info"><label class="setting-label"><%= translated("Online Chat Reasoning") %></label></div>
<div class="setting-control"><label><input type="checkbox" name="settings_ai[online_chat_disable_reasoning]" checked={@settings_editor.ai["online_chat_disable_reasoning"]} /> <%= translated("Disable reasoning output for the online chat model") %></label></div>
</div>
<div class="setting-row">
<div class="setting-info"><label class="setting-label"><%= translated("Online Title Model") %></label></div>
<div class="setting-control"><input type="text" list="settings-ai-online-models" name="settings_ai[online_title_model]" value={@settings_editor.ai["online_title_model"]} /></div>
@@ -266,6 +270,10 @@
<div class="setting-info"><label class="setting-label"><%= translated("Offline Chat Tools") %></label></div>
<div class="setting-control"><label><input type="checkbox" name="settings_ai[offline_chat_tools]" checked={@settings_editor.ai["offline_chat_tools"]} /> <%= translated("Enable tool calls for the offline chat model") %></label></div>
</div>
<div class="setting-row">
<div class="setting-info"><label class="setting-label"><%= translated("Offline Chat Reasoning") %></label></div>
<div class="setting-control"><label><input type="checkbox" name="settings_ai[offline_chat_disable_reasoning]" checked={@settings_editor.ai["offline_chat_disable_reasoning"]} /> <%= translated("Disable reasoning output for the offline chat model") %></label></div>
</div>
<div class="setting-row">
<div class="setting-info"><label class="setting-label"><%= translated("Offline Title Model") %></label></div>
<div class="setting-control"><input type="text" list="settings-ai-offline-models" name="settings_ai[offline_title_model]" value={@settings_editor.ai["offline_title_model"]} /></div>