fix: model selector works now
This commit is contained in:
@@ -169,6 +169,9 @@ defmodule BDS.AI do
|
|||||||
@spec available_chat_models(String.t() | nil) :: [map()]
|
@spec available_chat_models(String.t() | nil) :: [map()]
|
||||||
defdelegate available_chat_models(current_model \\ nil), to: Chat
|
defdelegate available_chat_models(current_model \\ nil), to: Chat
|
||||||
|
|
||||||
|
@spec effective_chat_model(BDS.AI.ChatConversation.t() | map() | nil) :: String.t() | nil
|
||||||
|
defdelegate effective_chat_model(conversation), to: Chat
|
||||||
|
|
||||||
@spec set_conversation_model(String.t(), String.t()) ::
|
@spec set_conversation_model(String.t(), String.t()) ::
|
||||||
{:ok, map()} | {:error, :not_found | Ecto.Changeset.t()}
|
{:ok, map()} | {:error, :not_found | Ecto.Changeset.t()}
|
||||||
defdelegate set_conversation_model(conversation_id, model_id), to: Chat
|
defdelegate set_conversation_model(conversation_id, model_id), to: Chat
|
||||||
|
|||||||
@@ -105,6 +105,15 @@ defmodule BDS.AI.Chat do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec effective_chat_model(ChatConversation.t() | map() | nil) :: String.t() | nil
|
||||||
|
def effective_chat_model(%ChatConversation{} = conversation) do
|
||||||
|
resolve_effective_chat_model(conversation.model)
|
||||||
|
end
|
||||||
|
|
||||||
|
def effective_chat_model(%{model: model}), do: resolve_effective_chat_model(model)
|
||||||
|
def effective_chat_model(%{"model" => model}), do: resolve_effective_chat_model(model)
|
||||||
|
def effective_chat_model(_conversation), do: resolve_effective_chat_model(nil)
|
||||||
|
|
||||||
@spec set_conversation_model(String.t(), String.t()) ::
|
@spec set_conversation_model(String.t(), String.t()) ::
|
||||||
{:ok, map()} | {:error, :not_found | Ecto.Changeset.t()}
|
{:ok, map()} | {:error, :not_found | Ecto.Changeset.t()}
|
||||||
def set_conversation_model(conversation_id, model_id)
|
def set_conversation_model(conversation_id, model_id)
|
||||||
@@ -282,6 +291,25 @@ defmodule BDS.AI.Chat do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp resolve_effective_chat_model(model) when is_binary(model) and model != "", do: model
|
||||||
|
|
||||||
|
defp resolve_effective_chat_model(_model) do
|
||||||
|
mode = if AI.airplane_mode?(), do: :airplane, else: :online
|
||||||
|
|
||||||
|
preference_key = if mode == :airplane, do: :airplane_chat, else: :chat
|
||||||
|
|
||||||
|
case Runtime.model_preference_value(preference_key) do
|
||||||
|
model when is_binary(model) and model != "" ->
|
||||||
|
model
|
||||||
|
|
||||||
|
_other ->
|
||||||
|
case AI.get_endpoint(mode) do
|
||||||
|
{:ok, %{model: model}} when is_binary(model) and model != "" -> model
|
||||||
|
_other -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp catalog_provider_name_map do
|
defp catalog_provider_name_map do
|
||||||
Repo.all(from provider in CatalogProvider, select: {provider.id, provider.name})
|
Repo.all(from provider in CatalogProvider, select: {provider.id, provider.name})
|
||||||
|> Map.new()
|
|> Map.new()
|
||||||
|
|||||||
@@ -15,12 +15,14 @@ defmodule BDS.Desktop.ShellLive.ChatEditor.MessageBuild do
|
|||||||
%ChatConversation{} = conversation ->
|
%ChatConversation{} = conversation ->
|
||||||
messages = AI.list_chat_messages(conversation.id)
|
messages = AI.list_chat_messages(conversation.id)
|
||||||
request = Map.get(assigns.chat_editor_requests, conversation.id)
|
request = Map.get(assigns.chat_editor_requests, conversation.id)
|
||||||
available_models = AI.available_chat_models(conversation.model)
|
effective_model = AI.effective_chat_model(conversation)
|
||||||
|
available_models = AI.available_chat_models(effective_model)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
id: conversation.id,
|
id: conversation.id,
|
||||||
title: conversation.title || translated("chat.newChat"),
|
title: conversation.title || translated("chat.newChat"),
|
||||||
model: conversation.model,
|
model: conversation.model,
|
||||||
|
effective_model: effective_model,
|
||||||
available_models: available_models,
|
available_models: available_models,
|
||||||
available_model_groups: ModelSelection.group_available_models(available_models),
|
available_model_groups: ModelSelection.group_available_models(available_models),
|
||||||
model_selector_open?:
|
model_selector_open?:
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
phx-click="toggle_chat_model_selector"
|
phx-click="toggle_chat_model_selector"
|
||||||
data-testid="chat-model-selector-button"
|
data-testid="chat-model-selector-button"
|
||||||
>
|
>
|
||||||
<span><%= @chat_editor.model || translated("chat.newChat") %></span>
|
<span><%= @chat_editor.effective_model || translated("chat.modelUnavailable") %></span>
|
||||||
<span class="chat-model-selector-caret">▾</span>
|
<span class="chat-model-selector-caret">▾</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
<button
|
<button
|
||||||
class={[
|
class={[
|
||||||
"chat-model-selector-option",
|
"chat-model-selector-option",
|
||||||
if(model.id == @chat_editor.model, do: "active")
|
if(model.id == @chat_editor.effective_model, do: "active")
|
||||||
]}
|
]}
|
||||||
type="button"
|
type="button"
|
||||||
phx-click="select_chat_model"
|
phx-click="select_chat_model"
|
||||||
|
|||||||
@@ -107,6 +107,7 @@
|
|||||||
"chat.welcomeTipTabs": "Beitragsstatistiken pro Jahr in Tabs mit Diagrammen",
|
"chat.welcomeTipTabs": "Beitragsstatistiken pro Jahr in Tabs mit Diagrammen",
|
||||||
"chat.role.you": "Du",
|
"chat.role.you": "Du",
|
||||||
"chat.role.assistant": "Assistent",
|
"chat.role.assistant": "Assistent",
|
||||||
|
"chat.modelUnavailable": "Kein Modell",
|
||||||
"chat.inputPlaceholder": "Nachricht eingeben...",
|
"chat.inputPlaceholder": "Nachricht eingeben...",
|
||||||
"chat.stop": "Stopp",
|
"chat.stop": "Stopp",
|
||||||
"chat.toolArguments": "Argumente",
|
"chat.toolArguments": "Argumente",
|
||||||
|
|||||||
@@ -107,6 +107,7 @@
|
|||||||
"chat.welcomeTipTabs": "Show post statistics by year in tabs with charts",
|
"chat.welcomeTipTabs": "Show post statistics by year in tabs with charts",
|
||||||
"chat.role.you": "You",
|
"chat.role.you": "You",
|
||||||
"chat.role.assistant": "Assistant",
|
"chat.role.assistant": "Assistant",
|
||||||
|
"chat.modelUnavailable": "No model",
|
||||||
"chat.inputPlaceholder": "Type a message...",
|
"chat.inputPlaceholder": "Type a message...",
|
||||||
"chat.stop": "Stop",
|
"chat.stop": "Stop",
|
||||||
"chat.toolArguments": "Arguments",
|
"chat.toolArguments": "Arguments",
|
||||||
|
|||||||
@@ -107,6 +107,7 @@
|
|||||||
"chat.welcomeTipTabs": "Muestre estadísticas por año en pestañas con gráficos",
|
"chat.welcomeTipTabs": "Muestre estadísticas por año en pestañas con gráficos",
|
||||||
"chat.role.you": "Tú",
|
"chat.role.you": "Tú",
|
||||||
"chat.role.assistant": "Asistente",
|
"chat.role.assistant": "Asistente",
|
||||||
|
"chat.modelUnavailable": "Sin modelo",
|
||||||
"chat.inputPlaceholder": "Escribe un mensaje...",
|
"chat.inputPlaceholder": "Escribe un mensaje...",
|
||||||
"chat.stop": "Detener",
|
"chat.stop": "Detener",
|
||||||
"chat.toolArguments": "Argumentos",
|
"chat.toolArguments": "Argumentos",
|
||||||
|
|||||||
@@ -107,6 +107,7 @@
|
|||||||
"chat.welcomeTipTabs": "Afficher les statistiques par année dans des onglets avec graphiques",
|
"chat.welcomeTipTabs": "Afficher les statistiques par année dans des onglets avec graphiques",
|
||||||
"chat.role.you": "Vous",
|
"chat.role.you": "Vous",
|
||||||
"chat.role.assistant": "Assistant IA",
|
"chat.role.assistant": "Assistant IA",
|
||||||
|
"chat.modelUnavailable": "Aucun modèle",
|
||||||
"chat.inputPlaceholder": "Saisissez un message...",
|
"chat.inputPlaceholder": "Saisissez un message...",
|
||||||
"chat.stop": "Arrêter",
|
"chat.stop": "Arrêter",
|
||||||
"chat.toolArguments": "Arguments",
|
"chat.toolArguments": "Arguments",
|
||||||
|
|||||||
@@ -107,6 +107,7 @@
|
|||||||
"chat.welcomeTipTabs": "Mostrare statistiche per anno in schede con grafici",
|
"chat.welcomeTipTabs": "Mostrare statistiche per anno in schede con grafici",
|
||||||
"chat.role.you": "Tu",
|
"chat.role.you": "Tu",
|
||||||
"chat.role.assistant": "Assistente",
|
"chat.role.assistant": "Assistente",
|
||||||
|
"chat.modelUnavailable": "Nessun modello",
|
||||||
"chat.inputPlaceholder": "Scrivi un messaggio...",
|
"chat.inputPlaceholder": "Scrivi un messaggio...",
|
||||||
"chat.stop": "Ferma",
|
"chat.stop": "Ferma",
|
||||||
"chat.toolArguments": "Argomenti",
|
"chat.toolArguments": "Argomenti",
|
||||||
|
|||||||
@@ -3565,7 +3565,7 @@ button svg * {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--vscode-foreground, inherit);
|
color: var(--vscode-foreground, inherit);
|
||||||
@@ -5145,7 +5145,7 @@ button svg * {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--vscode-foreground, inherit);
|
color: var(--vscode-foreground, inherit);
|
||||||
|
|||||||
@@ -2160,6 +2160,49 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
assert css =~ "position: static;"
|
assert css =~ "position: static;"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "chat editor model selector uses effective model for new chats and persists selection" do
|
||||||
|
assert :ok = AI.set_airplane_mode(true)
|
||||||
|
|
||||||
|
assert {:ok, _endpoint} =
|
||||||
|
AI.put_endpoint(:airplane, %{
|
||||||
|
url: "http://localhost:11434/v1",
|
||||||
|
api_key: nil,
|
||||||
|
model: "llama-default"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert :ok = AI.put_model_preference(:airplane_chat, "llama-current")
|
||||||
|
assert {:ok, conversation} = AI.start_chat(%{title: "New Chat"})
|
||||||
|
|
||||||
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
html =
|
||||||
|
render_click(view, "pin_sidebar_item", %{
|
||||||
|
"route" => "chat",
|
||||||
|
"id" => conversation.id,
|
||||||
|
"title" => conversation.title,
|
||||||
|
"subtitle" => "chat"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert html =~ ~s(data-testid="chat-model-selector-button")
|
||||||
|
assert html =~ "llama-current"
|
||||||
|
refute html =~ ~s(<span>New Chat</span><span class="chat-model-selector-caret">▾</span>)
|
||||||
|
|
||||||
|
selector_html = render_click(view, "toggle_chat_model_selector", %{})
|
||||||
|
assert selector_html =~ ~s(class="chat-model-selector-menu")
|
||||||
|
assert selector_html =~ ~s(data-testid="chat-model-selector-option")
|
||||||
|
assert selector_html =~ "llama-current"
|
||||||
|
|
||||||
|
css = File.read!(Path.expand("../../../priv/ui/app.css", __DIR__))
|
||||||
|
assert css =~ ".chat-panel-title {"
|
||||||
|
assert css =~ "overflow: visible;"
|
||||||
|
refute css =~ ".chat-panel-title {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 10px;\n overflow: hidden;"
|
||||||
|
|
||||||
|
render_click(view, "select_chat_model", %{"model" => "llama-next"})
|
||||||
|
|
||||||
|
assert AI.get_chat_conversation(conversation.id).model == "llama-next"
|
||||||
|
assert render(view) =~ "llama-next"
|
||||||
|
end
|
||||||
|
|
||||||
test "chat editor renders legacy model controls, collapsed tool pills, and dismissible A2UI surfaces" do
|
test "chat editor renders legacy model controls, collapsed tool pills, and dismissible A2UI surfaces" do
|
||||||
assert {:ok, conversation} = AI.start_chat(%{title: "Editor Chat", model: "gpt-4.1"})
|
assert {:ok, conversation} = AI.start_chat(%{title: "Editor Chat", model: "gpt-4.1"})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user