defmodule Mix.Tasks.LlmCore.Config.Validate do
@moduledoc """
Validates `llm_core.toml` configuration and prints provider availability.
Loads the merged TOML configuration, normalizes provider definitions, and
displays each provider's availability status and alias mappings. Also shows
the current routing table if one was loaded.
## Examples
mix llm_core.config.validate
mix llm_core.config.validate --path /custom/llm_core.toml
"""
use Mix.Task
@shortdoc "Validates llm_core TOML configuration"
@impl true
@spec run([String.t()]) :: :ok
def run(args) do
Mix.Task.run("app.start")
{opts, _, _} = OptionParser.parse(args, switches: [path: :string])
case LlmCore.Config.Loader.reload_providers(opts) do
{:ok, providers} ->
print_summary(providers)
{:error, reason} ->
Mix.raise("Failed to load configuration: #{inspect(reason)}")
end
end
defp print_summary(providers) do
IO.puts("Loaded #{map_size(providers)} providers:\n")
providers
|> Enum.sort_by(fn {id, _} -> id end)
|> Enum.each(fn {id, definition} ->
status = if definition.available?, do: "available", else: "unavailable"
aliases = Enum.join(definition.aliases, ", ")
kind_label =
if definition.provider_kind == :cli, do: "CLI", else: inspect(definition.module)
IO.puts("- #{id} (#{kind_label}) -> #{status} [aliases: #{aliases}]")
end)
case LlmCore.Config.Store.get_routing() do
{:ok, table} ->
IO.puts("\nRouting default: #{table.default.alias}")
Enum.each(table.rules, fn {task, entry} ->
IO.puts(" #{task} => #{entry.alias} (#{entry.mode})")
end)
_ ->
IO.puts("\nNo routing table loaded")
end
end
end