defmodule ArcanaWeb.InfoLive do @moduledoc """ LiveView for displaying Arcana configuration info. """ use Phoenix.LiveView import ArcanaWeb.DashboardComponents @impl true def mount(_params, session, socket) do repo = get_repo_from_session(session) {:ok, socket |> assign(repo: repo) |> assign(config_info: get_config_info()) |> assign(stats: nil)} end @impl true def handle_params(_params, _uri, socket) do {:noreply, load_data(socket)} end defp load_data(socket) do repo = socket.assigns.repo socket |> assign(stats: load_stats(repo)) end defp get_config_info do %{ repo: Application.get_env(:arcana, :repo), llm: format_llm_config(Application.get_env(:arcana, :llm)), embedding: format_embedding_config(Application.get_env(:arcana, :embedder, :local)), chunker: format_chunker_config(Application.get_env(:arcana, :chunker, :default)), reranker: format_reranker_config(Application.get_env(:arcana, :reranker)), vector_store: format_vector_store_config(), graph: format_graph_config(), raw: %{ embedder: Arcana.Config.redact(Application.get_env(:arcana, :embedder, :local)), llm: Arcana.Config.redact(Application.get_env(:arcana, :llm)), reranker: Arcana.Config.redact(Application.get_env(:arcana, :reranker)), chunker: Arcana.Config.redact(Application.get_env(:arcana, :chunker, :default)), graph: Arcana.Config.redact(Application.get_env(:arcana, :graph, [])), vector_store: Application.get_env(:arcana, :vector_store, :pgvector) } } end defp format_llm_config(nil), do: %{configured: false} defp format_llm_config(llm) when is_function(llm) do %{configured: true, type: "Function"} end defp format_llm_config(llm) do case llm do %{__struct__: module} = struct -> %{ configured: true, type: module |> Module.split() |> List.last(), model: Map.get(struct, :model, "unknown") } {model, _opts} when is_binary(model) -> %{configured: true, type: "Req.LLM", model: model} {model, _opts} when is_atom(model) -> %{configured: true, type: "Req.LLM", model: Atom.to_string(model)} model when is_binary(model) -> %{configured: true, type: "Req.LLM", model: model} model when is_atom(model) -> %{configured: true, type: "Req.LLM", model: Atom.to_string(model)} _ -> %{configured: true, type: "Custom"} end end defp format_embedding_config(:local), do: %{type: :local, model: "BAAI/bge-small-en-v1.5"} defp format_embedding_config(:openai), do: %{type: :openai, model: "text-embedding-3-small"} defp format_embedding_config({:local, opts}) do %{type: :local, model: Keyword.get(opts, :model, "BAAI/bge-small-en-v1.5")} end defp format_embedding_config({:openai, opts}) do %{type: :openai, model: Keyword.get(opts, :model, "text-embedding-3-small")} end defp format_embedding_config({:custom, _fun}), do: %{type: :custom} defp format_embedding_config({:custom, _fun, _opts}), do: %{type: :custom} defp format_embedding_config({module, opts}) when is_atom(module) and is_list(opts) do %{type: :custom_module, module: module, opts: Arcana.Config.do_redact(opts)} end defp format_embedding_config(module) when is_atom(module) do %{type: :custom_module, module: module} end defp format_embedding_config(other), do: %{type: :unknown, raw: inspect(other)} defp format_reranker_config(nil), do: %{module: Arcana.Reranker.LLM, configured: false} defp format_reranker_config(module) when is_atom(module), do: %{module: module, configured: true} defp format_reranker_config({module, opts}) when is_atom(module) and is_list(opts) do %{module: module, opts: Arcana.Config.do_redact(opts), configured: true} end defp format_reranker_config(fun) when is_function(fun) do %{type: :function, configured: true} end defp format_reranker_config(other), do: %{type: :unknown, raw: Arcana.Config.do_redact(other), configured: true} defp format_chunker_config(:default), do: %{type: :default, chunk_size: 512, chunk_overlap: 100} defp format_chunker_config({:default, opts}) do %{ type: :default, chunk_size: Keyword.get(opts, :chunk_size, 512), chunk_overlap: Keyword.get(opts, :chunk_overlap, 100) } end defp format_chunker_config(fun) when is_function(fun), do: %{type: :function} defp format_chunker_config({module, opts}) when is_atom(module) and is_list(opts) do %{type: :custom, module: module, opts: Arcana.Config.do_redact(opts)} end defp format_chunker_config(module) when is_atom(module), do: %{type: :custom, module: module} defp format_chunker_config(_other), do: %{type: :unknown} defp format_vector_store_config do store = Application.get_env(:arcana, :vector_store, :pgvector) case store do :pgvector -> %{type: :pgvector, description: "PostgreSQL with pgvector extension"} :memory -> %{type: :memory, description: "In-memory vector store"} module when is_atom(module) -> %{type: :custom, module: module} _ -> %{type: :unknown} end end defp format_graph_config do config = Arcana.Graph.config() graph_opts = Application.get_env(:arcana, :graph, []) extractor = cond do config[:extractor] -> format_extractor(config[:extractor]) graph_opts[:extractor] -> format_extractor(graph_opts[:extractor]) true -> nil end entity_extractor = cond do config[:entity_extractor] -> format_extractor(config[:entity_extractor]) graph_opts[:entity_extractor] -> format_extractor(graph_opts[:entity_extractor]) true -> %{type: :ner, description: "Built-in NER (default)"} end relationship_extractor = cond do config[:relationship_extractor] -> format_extractor(config[:relationship_extractor]) graph_opts[:relationship_extractor] -> format_extractor(graph_opts[:relationship_extractor]) true -> nil end community_detector = cond do config[:community_detector] -> format_community_detector(config[:community_detector]) graph_opts[:community_detector] -> format_community_detector(graph_opts[:community_detector]) true -> %{type: :leiden, description: "Leiden (default)"} end %{ enabled: config.enabled, community_levels: config.community_levels, resolution: config.resolution, store: Application.get_env(:arcana, :graph_store, :ecto), extractor: extractor, entity_extractor: entity_extractor, relationship_extractor: relationship_extractor, community_detector: community_detector } end defp format_extractor(nil), do: nil defp format_extractor(:ner), do: %{type: :ner, description: "Built-in NER"} defp format_extractor(:llm), do: %{type: :llm, description: "LLM-based extraction"} defp format_extractor({module, opts}) when is_atom(module) and is_list(opts) do module_name = module |> Module.split() |> List.last() %{type: :custom, module: module_name, opts: Arcana.Config.do_redact(opts)} end defp format_extractor(module) when is_atom(module) do module_name = module |> Module.split() |> List.last() %{type: :module, module: module_name} end defp format_extractor(_other), do: %{type: :unknown} defp format_community_detector(nil), do: nil defp format_community_detector(:leiden), do: %{type: :leiden, description: "Leiden"} defp format_community_detector({module, opts}) when is_atom(module) and is_list(opts) do module_name = module |> Module.split() |> List.last() %{type: :custom, module: module_name, opts: Arcana.Config.do_redact(opts)} end defp format_community_detector(module) when is_atom(module) do module_name = module |> Module.split() |> List.last() %{type: :module, module: module_name} end defp format_community_detector(_other), do: %{type: :unknown} defp raw_config_section(assigns) do config_text = """ config :arcana, repo: #{inspect(assigns.config_info.repo)}, embedder: #{inspect(assigns.config_info.raw.embedder)}, llm: #{inspect(assigns.config_info.raw.llm)}, chunker: #{inspect(assigns.config_info.raw.chunker)}, reranker: #{if assigns.config_info.raw.reranker, do: inspect(assigns.config_info.raw.reranker), else: "nil # defaults to Arcana.Reranker.LLM"}, vector_store: #{inspect(assigns.config_info.raw.vector_store)}, graph: #{inspect(assigns.config_info.raw.graph)} """ assigns = assign(assigns, :config_text, config_text) ~H"""
<%= @config_text %>
View current Arcana configuration including embedding, LLM, and chunking settings.
<%= inspect(@config_info.embedding.module) %>
<%= inspect(@config_info.chunker.module) %>
<%= inspect(@config_info.reranker[:module] || @config_info.reranker[:type]) %>
<%= inspect(@config_info.vector_store.module) %>
<%= inspect(@config_info.repo) %>