diff --git a/config/runtime.exs b/config/runtime.exs index 526614ae..9f03bc1b 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -23,10 +23,20 @@ end # DeepSeek LLM (used by Towerops.LLM.DeepSeek for insight enrichment). # DEEPSEEK_API_KEY is optional — when missing, enrichment is skipped and # insights still display without an LLM-generated summary. +# Helper: treat empty-string env vars as missing (the DEEPSEEK_MODEL +# secret ships as "" by default, which would otherwise bypass `||`). +deepseek_env = fn name, fallback -> + case System.get_env(name) do + nil -> fallback + "" -> fallback + value -> value + end +end + config :towerops, Towerops.LLM.DeepSeek, api_key: System.get_env("DEEPSEEK_API_KEY"), - base_url: System.get_env("DEEPSEEK_BASE_URL") || "https://api.deepseek.com/v1", - model: System.get_env("DEEPSEEK_MODEL") || "deepseek-v4-pro" + base_url: deepseek_env.("DEEPSEEK_BASE_URL", "https://api.deepseek.com/v1"), + model: deepseek_env.("DEEPSEEK_MODEL", "deepseek-v4-pro") config :towerops, ToweropsWeb.Endpoint, http: [port: String.to_integer(System.get_env("PORT", "4000"))] diff --git a/lib/towerops/llm/deepseek.ex b/lib/towerops/llm/deepseek.ex index af63b1df..f5119b30 100644 --- a/lib/towerops/llm/deepseek.ex +++ b/lib/towerops/llm/deepseek.ex @@ -20,8 +20,8 @@ defmodule Towerops.LLM.DeepSeek do def complete(messages, opts \\ []) do cfg = Application.get_env(:towerops, __MODULE__, []) api_key = opts[:api_key] || cfg[:api_key] - base_url = opts[:base_url] || cfg[:base_url] || @default_base_url - model = opts[:model] || cfg[:model] || @default_model + base_url = present(opts[:base_url]) || present(cfg[:base_url]) || @default_base_url + model = present(opts[:model]) || present(cfg[:model]) || @default_model if blank?(api_key) do {:error, :missing_api_key} @@ -34,6 +34,13 @@ defmodule Towerops.LLM.DeepSeek do defp blank?(""), do: true defp blank?(s) when is_binary(s), do: String.trim(s) == "" + # Returns the value if non-empty, nil otherwise — lets `||` cascade past + # blank env-var values (a `""` env var would otherwise short-circuit + # `present(opts[:x]) || present(cfg[:x]) || @default_x`). + defp present(nil), do: nil + defp present(s) when is_binary(s), do: if(String.trim(s) == "", do: nil, else: s) + defp present(other), do: other + defp request(base_url, api_key, model, messages, opts) do body = %{ model: model, diff --git a/lib/towerops_web/live/insights_live/index.html.heex b/lib/towerops_web/live/insights_live/index.html.heex index e067d1bd..56330be7 100644 --- a/lib/towerops_web/live/insights_live/index.html.heex +++ b/lib/towerops_web/live/insights_live/index.html.heex @@ -136,7 +136,7 @@ id="bulk-actions" class="mt-4 flex items-center gap-3 rounded-lg bg-indigo-50 px-4 py-3 dark:bg-indigo-900/20" > - + {MapSet.size(@selected_ids)} selected